From 19ef488573703f51e19850e9f77b11cb119c1c3d Mon Sep 17 00:00:00 2001 From: ddemeyer Date: Wed, 15 Sep 2021 18:42:50 +0200 Subject: [PATCH] #115 Merged cmdlets and tests of Session (with PesterV5, 132)... Broke compatiblity regarding ParameterSets based on Issuer, TimeoutIssue, TimeoutService and PSCredential... Tests are working... --- Doc/TheExecution-ISHRemote-7.0.md | 5 +- .../Cmdlets/Session/NewIshSession.Tests.ps1 | 41 +++-- .../Cmdlets/Session/NewIshSession.cs | 36 +--- .../Cmdlets/Session/TestIshSession.Tests.ps1 | 75 +++++++++ .../Cmdlets/Session/TestIshSession.cs | 158 ++++++++++++++++++ .../Trisoft.ISHRemote/GlobalSuppressions.cs | 2 +- .../ISHRemote.PesterSetup.ps1 | 45 +++-- .../Objects/Public/IshSession.cs | 102 ++++------- 8 files changed, 319 insertions(+), 145 deletions(-) create mode 100644 Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.Tests.ps1 create mode 100644 Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.cs diff --git a/Doc/TheExecution-ISHRemote-7.0.md b/Doc/TheExecution-ISHRemote-7.0.md index 1d94dd1e..64a7ea59 100644 --- a/Doc/TheExecution-ISHRemote-7.0.md +++ b/Doc/TheExecution-ISHRemote-7.0.md @@ -71,20 +71,23 @@ Publishing problem perhaps, as copying %USER%\.nuget\packages\system.servicemode 1. Cleaned up `NewIshSession.Tests.ps1` noticing the HTTPS/SSL (even TLS1.3) is missing, Timeout parameters are uncertain, PSCredential is a gap... but test has been cleaned up given 48 successes. Main branch ISHRemote v0.14 had all Pester tests refactored to Pester 5.3.0 (see #132)... still conditional/skip flags required to distuingish between WCF/ASMX/OpenAPI 6. `TrisoftCmdlet.cs` says `[assembly: ComVisible(false)]` ... brrr? Why? Removed 1. Build `Debug` in the same way as `Release` with copied `Scripts` folder (also the only folder for PSScriptAnalyzer to enforce) so that `ISHRemote.PesterSetup.ps1` can keep pointing to the `Debug` packaged ISHRemote. +2. Aligned Session, so `New-IshSession` and `Test-IshSession` with upcoming 0.14 (#132) including PesterV5 tests. ## Next 1. `New-IshSession -WsBaseUrl .../ISHWS/ -IshUserName ... -IshPassword -Protocol [AsmxAuthenticationContext (default) | OpenApiBasicAuthentication | OpenApiOpenConnectId]` so Protocol as a parameter to use in Switch-cases in every cmdlet on how to route the code -7. Migrate `*-IshFolder` cmdlets as you need them for almost all tests anyway. Easy to do performance runs on Add-IshFolder and Remove-IshFolder. +7. Migrate `*-IshFolder` cmdlets as you need them for almost all tests anyway. Easy to do performance runs on Add-IshFolder and Remove-IshFolder. Later we have the following API25 to API30 mapping 1. Folder25.Create -> API30.Create (ready) 2. Folder25.RetrieveMetadataByIshFolderRefs -> API30.GetFolderList (NotImplemented planned for PI20.4) 3. Folder25.Delete -> API30.DeleteFolder (ready) 4. Folder25.GetMetadataByIshFolderRef -> API30.GetFolder (ready) 5. Folder25.GetMetadata -> API30.GetFolderByFolderPath, perhaps GetRootFolderList (NotPlanned) 6. Folder25.GetSubFoldersByIshFolderRef -> API30.GetFolderObjectList (ready) +1. Next is add all SoapClient proxies 8. Is `CertificateValidationHelper.cs` and `ServicePointManagerHelper.cs` still the way to do certificate bypass? Make sure TLS 1.3 is activated (possible since net4.8 and higher) 9. Parameter `-PSCredential` doesn't work because of `SecureString` being Windows cryptography only according to https://github.com/PowerShell/PowerShell/issues/1654 ... what is next? Needs alignment with https://devblogs.microsoft.com/powershell/secretmanagement-and-secretstore-release-candidate-2/ 1. Also a `New-IshSession` scheduled task code sample like in the past using Windows-only `ConvertTo-SecureString` is required, perhaps over Secret Management. 10. Upon WCF Proxy retrieval from IshSession object, there used to be a `VerifyTokenValidity` that would check the authentication, and potentially re-authenticate all proxies. For `AuthenticationContext` we only now it is valid for 7 days, so ISHRemote could track that or the script using ISHRemote should handle that for now. Actually if you pass `AuthenticationContext` by ref on every call it gets refreshed anyway, so only a problem if IshSession is not used for 7+ days. +11. ISHRemote 0.x branch replace bad quote `“` with proper quote `"` in `*.Tests.ps1`, for example NewIshSession.Tests.ps1 # Performance diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.Tests.ps1 b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.Tests.ps1 index 31aeeee6..ebd3053d 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.Tests.ps1 +++ b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.Tests.ps1 @@ -1,21 +1,21 @@ BeforeAll { - Write-Host ("`r`nLoading ISHRemote.PesterSetup.ps1 for MyCommand[" + $MyInvocation.MyCommand + "]...") - Write-Host BeforeAll loading the extra script + $cmdletName = "New-IshSession" + Write-Host ("`r`nLoading ISHRemote.PesterSetup.ps1 over BeforeAll-block for MyCommand[" + $cmdletName + "]...") . (Join-Path (Split-Path -Parent $PSCommandPath) "\..\..\ISHRemote.PesterSetup.ps1") - - Write-Host "Initializing Test Data and Variables" + + Write-Host ("Running "+$cmdletName+" Test Data and Variables initialization") + $ishSession = $null # Resetting generic $ishSession } Describe "New-IshSession" -Tags "Read" { - Context "New-IshSession ISHDeploy::Enable-ISHIntegrationSTSInternalAuthentication/Prepare-SupportAccess.ps1" { It "Parameter WsBaseUrl contains 'SDL' (legacy script)" -skip { $ishSession = New-IshSession -WsBaseUrl https://example.com/ISHWS/SDL/ -IshUserName x -IshPassword y - $ishSession.ServerVersion | Should -Not BeNullOrEmpty + $ishSession.ServerVersion | Should -Not -BeNullOrEmpty } It "Parameter WsBaseUrl contains 'Internal' (ISHDeploy)" -skip { $ishSession = New-IshSession -WsBaseUrl https://example.com/ISHWS/Internal/ -IshUserName x -IshPassword y - $ishSession.ServerVersion | Should -Not BeNullOrEmpty + $ishSession.ServerVersion | Should -Not -BeNullOrEmpty } } @@ -90,8 +90,8 @@ Describe "New-IshSession" -Tags "Read" { It "IshSession.ServerVersion contains 4 dot-seperated parts" { $ishSession.ServerVersion.Split(".").Length | Should -Be 4 } - It "IshSession.Timeout defaults to 20s" { - $ishSession.Timeout.TotalMilliseconds -eq 20000 | Should -Be $true + It "IshSession.Timeout defaults to 30m" { + $ishSession.Timeout.TotalMinutes -eq 30 | Should -Be $true } It "IshSession.StrictMetadataPreference" { $ishSession.StrictMetadataPreference | Should -Be "Continue" @@ -111,7 +111,7 @@ Describe "New-IshSession" -Tags "Read" { It "WsBaseUrl without ending slash" { # .NET throws unhandy "Reference to undeclared entity 'raquo'." error $webServicesBaseUrlWithoutEndingSlash = $webServicesBaseUrl.Substring(0,$webServicesBaseUrl.Length-1) - { New-IshSession -WsBaseUrl $webServicesBaseUrlWithoutEndingSlash -IshUserName $ishUserName -IshPassword $ishPassword } | Should -Not -Throw + { $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrlWithoutEndingSlash -IshUserName $ishUserName -IshPassword $ishPassword } | Should -Not -Throw } } @@ -120,7 +120,7 @@ Describe "New-IshSession" -Tags "Read" { { $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -Timeout "INVALIDTIMEOUT" } | Should -Throw } It "IshSession.Timeout set to 30s" { - $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -Timeout (New-TimeSpan -Seconds 60) + $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -Timeout (New-TimeSpan -Seconds 60) -WarningAction Ignore -ErrorAction Ignore $ishSession.Timeout.TotalMilliseconds | Should -Be "60000" } It "IshSession.Timeout on INVALID url set to 1ms execution" { @@ -134,11 +134,11 @@ Describe "New-IshSession" -Tags "Read" { Context "New-IshSession IgnoreSslPolicyErrors" { It "Parameter IgnoreSslPolicyErrors specified positive flow" { - $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -IgnoreSslPolicyErrors + $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -IgnoreSslPolicyErrors -WarningAction Ignore $ishSession.ServerVersion | Should -Not -BeNullOrEmpty $ishSession.ServerVersion.Split(".").Length | Should -Be 4 } - It "Parameter IgnoreSslPolicyErrors specified negative flow (segment-one-url)" -skip { + It "Parameter IgnoreSslPolicyErrors specified negative flow (segment-one-url)" -Skip { # replace hostname like machinename.somedomain.com to machinename only, marked as skipped for non-development machines $slash1Position = $webServicesBaseUrl.IndexOf("/") $slash2Position = $webServicesBaseUrl.IndexOf("/",$slash1Position+1) @@ -147,11 +147,11 @@ Describe "New-IshSession" -Tags "Read" { $computername = $hostname.Substring(0,$hostname.IndexOf(".")) $webServicesBaseUrlToComputerName = $webServicesBaseUrl.Replace($hostname,$computername) $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrlToComputerName -IshUserName $ishUserName -IshPassword $ishPassword -IgnoreSslPolicyErrors - $ishSession.ServerVersion | Should -Not BeNullOrEmpty + $ishSession.ServerVersion | Should -Not -BeNullOrEmpty $ishSession.ServerVersion.Split(".").Length | Should -Be 4 $ishSession.Dispose() } - <# It "Parameter IgnoreSslPolicyErrors specified negative flow (Resolve-DnsName)" -skip { + <# It "Parameter IgnoreSslPolicyErrors specified negative flow (Resolve-DnsName)" -Skip { # replace hostname like example.com with ip-address $slash1Position = $webServicesBaseUrl.IndexOf("/") $slash2Position = $webServicesBaseUrl.IndexOf("/",$slash1Position+1) @@ -160,8 +160,8 @@ Describe "New-IshSession" -Tags "Read" { $ipAddress = Resolve-DnsName –Name $hostname # only available on Windows Server 2012 R2 and Windows 8.1 $webServicesBaseUrlToIpAddress = $webServicesBaseUrl.Replace($hostname,$ipAddress) $ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrlToIpAddress -IshUserName $ishUserName -IshPassword $ishPassword -IgnoreSslPolicyErrors - $ishSession.ServerVersion | Should Not BeNullOrEmpty - $ishSession.ServerVersion.Split(".").Length | Should Be 4 + $ishSession.ServerVersion | Should -Not -BeNullOrEmpty + $ishSession.ServerVersion.Split(".").Length | Should -Be 4 $ishSession.Dispose() } #> } @@ -228,5 +228,12 @@ Describe "New-IshSession" -Tags "Read" { It "IshSession.UserGroup25" { -not (Get-Member -inputobject $ishSession -Membertype Properties -Name UserGroup25) | Should -Be $true } + It "IshSession.UserRole25" { + -not (Get-Member -inputobject $ishSession -Membertype Properties -Name UserRole25) | Should -Be $true + } } +} + +AfterAll { + Write-Host ("Running "+$cmdletName+" Test Data and Variables cleanup") } \ No newline at end of file diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.cs b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.cs index b4339565..25063989 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/NewIshSession.cs @@ -38,12 +38,6 @@ namespace Trisoft.ISHRemote.Cmdlets.Session /// /// /// - /// $ishSession = New-IshSession -WsBaseUrl "https://example.com/ISHWS/" - /// - /// Building a session for the chosen service based on Active Directory authentication. An implicit NetworkCredential object will be passed for authentication to the service. - /// - /// - /// /// $ishSession = New-IshSession -WsBaseUrl "https://example.com/ISHWS/" -PSCredential "Admin" /// /// Iteratively the New-IshSession line with PSCredential parameter holding a string representation will prompt you for a password. @@ -59,23 +53,14 @@ namespace Trisoft.ISHRemote.Cmdlets.Session public sealed class NewIshSession : SessionCmdlet { /// - /// SDL Tridion Docs Content Manager web services main URL. Note that the URL is case-sensitive and should end with an ending slash! For example: "https://example.com/ISHWS/" + /// Tridion Docs Content Manager web services main URL. Note that the URL is case-sensitive and should end with an ending slash! For example: "https://example.com/ISHWS/" /// [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "PSCredential")] [ValidateNotNullOrEmpty] public string WsBaseUrl { get; set; } /// - /// Standard PowerShell Credential class - /// - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "PSCredential")] - [ValidateNotNullOrEmpty] - [Credential] - public PSCredential PSCredential { get; set; } - - /// - /// Username to login into SDL Tridion Docs Content Manager. When left empty, fall back to ActiveDirectory. + /// Username to login into Tridion Docs Content Manager. When left empty, fall back to ActiveDirectory. /// [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] [ValidateNotNullOrEmpty] @@ -86,7 +71,7 @@ public string IshUserName } /// - /// Password to login into SDL Tridion Docs Content Manager + /// Password to login into Tridion Docs Content Manager /// [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] [AllowEmptyString] @@ -97,9 +82,9 @@ public string IshPassword } /// - /// Timeout value expressed as TimeSpan, that controls Send/Receive timeouts of HttpClient when downloading content like connectionconfiguration.xml Defaults to 20 seconds. + /// Timeout value expressed as TimeSpan, that controls Send/Receive timeouts of HttpClient when processing ASMX services or downloading content like connectionconfiguration.xml Defaults to 30 minutes. /// - [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false)] + [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] public TimeSpan Timeout { get { return _timeout; } @@ -109,7 +94,7 @@ public TimeSpan Timeout /// /// IgnoreSslPolicyErrors presence indicates that a custom callback will be assigned to ServicePointManager.ServerCertificateValidationCallback. Defaults false of course, as this is creates security holes! But very handy for Fiddler usage though. /// - [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false)] + [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] public SwitchParameter IgnoreSslPolicyErrors { get { return _ignoreSslPolicyErrors; } @@ -119,7 +104,7 @@ public SwitchParameter IgnoreSslPolicyErrors #region Private fields private string _ishUserName = null; private string _ishPassword = null; - private TimeSpan _timeout = new TimeSpan(0, 0, 20); // up to 15s for a DNS lookup according to https://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout%28v=vs.110%29.aspx + private TimeSpan _timeout = new TimeSpan(0, 30, 0); // up to 15s for a DNS lookup according to https://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout%28v=vs.110%29.aspx private bool _ignoreSslPolicyErrors = false; #endregion @@ -128,13 +113,6 @@ protected override void ProcessRecord() try { int ishPasswordLength = _ishPassword == null ? 0 : _ishPassword.Length; - if (PSCredential != null) - { - _ishUserName = PSCredential.UserName; - WriteWarning($"PSCredential.Password conversion might be wrong because of Windows only cryptography!"); - _ishPassword = PSCredential.Password.ToString(); - } - WriteVerbose($"Connecting to WsBaseUrl[{WsBaseUrl}] IshUserName[{_ishUserName}] IshPassword[" + new string('*', ishPasswordLength) + "]"); WriteDebug($"Connecting to WsBaseUrl[{WsBaseUrl}] IshUserName[{_ishUserName}] IshPassword[" + new string('*', ishPasswordLength) + $"] Timeout[{_timeout}] IgnoreSslPolicyErrors[{_ignoreSslPolicyErrors}]"); IshSession ishSession = new IshSession(Logger, WsBaseUrl, _ishUserName, _ishPassword, _timeout, _ignoreSslPolicyErrors); diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.Tests.ps1 b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.Tests.ps1 new file mode 100644 index 00000000..6deb0c28 --- /dev/null +++ b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.Tests.ps1 @@ -0,0 +1,75 @@ +BeforeAll { + $cmdletName = "Test-IshSession" + Write-Host ("`r`nLoading ISHRemote.PesterSetup.ps1 over BeforeAll-block for MyCommand[" + $cmdletName + "]...") + . (Join-Path (Split-Path -Parent $PSCommandPath) "\..\..\ISHRemote.PesterSetup.ps1") + + Write-Host ("Running "+$cmdletName+" Test Data and Variables initialization") +} + +Describe "Test-IshSession" -Tags "Read" { + Context "Test-IshSession ISHDeploy::Enable-ISHIntegrationSTSInternalAuthentication/Prepare-SupportAccess.ps1" { + It "Parameter WsBaseUrl contains 'SDL' (legacy script)" -Skip { + Test-IshSession -WsBaseUrl https://example.com/ISHWS/SDL/ -IshUserName x -IshPassword y | Should -Be $true + } + It "Parameter WsBaseUrl contains 'Internal' (ISHDeploy)" -Skip { + Test-IshSession -WsBaseUrl https://example.com/ISHWS/Internal/ -IshUserName x -IshPassword y | Should -Be $true + } + } + + Context "Test-IshSession UserNamePassword" { + It "Parameter WsBaseUrl invalid" { + Test-IshSession -WsBaseUrl "http:///INVALIDWSBASEURL" -IshUserName "INVALIDISHUSERNAME" -IshPassword "INVALIDISHPASSWORD" | Should -Be $false + } + It "Parameter IshUserName invalid" { + Test-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName "INVALIDISHUSERNAME" -IshPassword "INVALIDISHPASSWORD" | Should -Be $false + } + It "Parameter IshPassword specified" { + Test-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword "INVALIDISHPASSWORD" | Should -Be $false + } + } + + Context "Test-IshSession returns bool" { + BeforeAll { + $ishSessionResult = Test-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword + } + It "GetType()" { + $ishSessionResult.GetType().Name | Should -BeExactly "Boolean" + } + } + + Context "Test-IshSession WsBaseUrl without ending slash" { + It "WsBaseUrl without ending slash" { + # .NET throws unhandy "Reference to undeclared entity 'raquo'." error + $webServicesBaseUrlWithoutEndingSlash = $webServicesBaseUrl.Substring(0,$webServicesBaseUrl.Length-1) + Test-IshSession -WsBaseUrl $webServicesBaseUrlWithoutEndingSlash -IshUserName $ishUserName -IshPassword $ishPassword | Should -Be $true + } + } + + Context "Test-IshSession Timeout" { + It "Parameter Timeout Invalid" { + { Test-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -Timeout "INVALIDTIMEOUT" } | Should -Throw + } + It "IshSession.Timeout on INVALID url set to 1ms execution" { + # TaskCanceledException: A task was canceled. + $invalidWebServicesBaseUrl = $webServicesBaseUrl -replace "://", "://INVALID" + Test-IshSession -WsBaseUrl $invalidWebServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -Timeout (New-Object TimeSpan(0,0,0,0,1)) | Should -Be $false + } + } + + Context "Test-IshSession IgnoreSslPolicyErrors" { + It "Parameter IgnoreSslPolicyErrors specified negative flow (segment-one-url)" -Skip { + # replace hostname like machinename.somedomain.com to machinename only, marked as skipped for non-development machines + $slash1Position = $webServicesBaseUrl.IndexOf("/") + $slash2Position = $webServicesBaseUrl.IndexOf("/",$slash1Position+1) + $slash3Position = $webServicesBaseUrl.IndexOf("/",$slash2Position+1) + $hostname = $webServicesBaseUrl.Substring($slash2Position+1,$slash3Position-$slash2Position-1) + $computername = $hostname.Substring(0,$hostname.IndexOf(".")) + $webServicesBaseUrlToComputerName = $webServicesBaseUrl.Replace($hostname,$computername) + Test-IshSession -WsBaseUrl $webServicesBaseUrlToComputerName -IshUserName $ishUserName -IshPassword $ishPassword -IgnoreSslPolicyErrors -WarningAction Ignore | Should -Be $true + } + } +} + +AfterAll { + Write-Host ("Running "+$cmdletName+" Test Data and Variables cleanup") +} \ No newline at end of file diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.cs b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.cs new file mode 100644 index 00000000..462c3f61 --- /dev/null +++ b/Source/ISHRemote/Trisoft.ISHRemote/Cmdlets/Session/TestIshSession.cs @@ -0,0 +1,158 @@ +/* +* Copyright (c) 2014 All Rights Reserved by the SDL Group. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Management.Automation; +using Trisoft.ISHRemote.Objects; +using Trisoft.ISHRemote.Objects.Public; +using Trisoft.ISHRemote.Exceptions; +using System.Reflection; +using Trisoft.ISHRemote.HelperClasses; +using System.Security; + +namespace Trisoft.ISHRemote.Cmdlets.Session +{ + /// + /// The Test-IshSession cmdlet creates a new IshSession object using the parameters that are provided. + /// The Test-IshSession cmdlet internal creates a minimal IshSession object using the parameters that are provided. + /// Tests the WebServices (ISHWS-activation) and validates the credentials in the 'InfoShare' database (ConnectionString-activation). + /// + /// + /// + /// Test-IshSession -WsBaseUrl "https://example.com/ISHWS/" -IshUserName "admin" -IshPassword "admin" + /// + /// Building a session for the chosen service based on username/password authentication. The username/password will be used to build a NetworkCredential object to pass for authentication to the service. + /// + /// + /// + /// Test-IshSession -WsBaseUrl "https://example.com/ISHWS/" + /// + /// Building a session for the chosen service based on Active Directory authentication. An implicit NetworkCredential object will be passed for authentication to the service. + /// + /// + /// + /// Test-IshSession -WsBaseUrl "https://example.com/ISHWS/" -IshUserName "admin" -IshPassword "admin" -Timeout (New-TimeSpan -Seconds 30) + /// + /// Building a session for the chosen service based on username/password authentication. The Timeout parameter, expressed as TimeSpan object, controls Send/Receive timeouts of HttpClient when downloading content like connectionconfiguration.xml. + /// + /// + /// + /// Test-IshSession -WsBaseUrl "https://example.com/ISHWS/" -IshUserName "admin" -IshPassword "admin" -IgnoreSslPolicyErrors + /// + /// IgnoreSslPolicyErrors presence indicates that a custom callback will be assigned to ServicePointManager.ServerCertificateValidationCallback. Defaults false of course, as this is creates security holes! But very handy for Fiddler usage though. + /// + /// + /// + /// Test-IshSession -WsBaseUrl "https://example.com/ISHWS/" -IshUserName "admin" -IshPassword "admin" -IgnoreSslPolicyErrors -Verbose + /// Invoke-WebRequest -Uri "https://example.com/ISHCM/InfoShareAuthor.asp" -UseBasicParsing + /// + /// These lines of code activate and hence test the WebServices (ISHWS-activation) and validates the credentials in the 'InfoShare' database (ConnectionString-activation). The extra .ASP line triggers WebClient (ISHCM-activation) and the COM+ application (Trisoft-InfoShare-Author). + /// + [Cmdlet(VerbsDiagnostic.Test, "IshSession", SupportsShouldProcess = false)] + [OutputType(typeof(bool))] + public sealed class TestIshSession : SessionCmdlet + { + /// + /// Tridion Docs Content Manager web services main URL. Note that the URL is case-sensitive and should end with an ending slash! For example: "https://example.com/ISHWS/" + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] + [ValidateNotNullOrEmpty] + public string WsBaseUrl { get; set; } + + /// + /// Username to login into Tridion Docs Content Manager. When left empty, fall back to ActiveDirectory. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] + [ValidateNotNullOrEmpty] + public string IshUserName + { + get { return _ishUserName; } + set { _ishUserName = value; } + } + + /// + /// Password to login into Tridion Docs Content Manager + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] + [AllowEmptyString] + public string IshPassword + { + get { return _ishPassword; } + set { _ishPassword = value; } + } + + /// + /// Timeout value expressed as TimeSpan, that controls Send/Receive timeouts of HttpClient when processing ASMX services or downloading content like connectionconfiguration.xml Defaults to 30 minutes. + /// + [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] + public TimeSpan Timeout + { + get { return _timeout; } + set { _timeout = value; } + } + + /// + /// IgnoreSslPolicyErrors presence indicates that a custom callback will be assigned to ServicePointManager.ServerCertificateValidationCallback. Defaults false of course, as this is creates security holes! But very handy for Fiddler usage though. + /// + [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")] + public SwitchParameter IgnoreSslPolicyErrors + { + get { return _ignoreSslPolicyErrors; } + set { _ignoreSslPolicyErrors = value; } + } + + #region Private fields + private string _ishUserName = null; + private string _ishPassword = null; + private TimeSpan _timeout = new TimeSpan(0, 30, 0); // up to 15s for a DNS lookup according to https://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout%28v=vs.110%29.aspx + private bool _ignoreSslPolicyErrors = false; + + #endregion + protected override void ProcessRecord() + { + try + { + int ishPasswordLength = _ishPassword == null ? 0 : _ishPassword.Length; + WriteVerbose($"Connecting to WsBaseUrl[{WsBaseUrl}] IshUserName[{_ishUserName}] IshPassword[" + new string('*', ishPasswordLength) + "]"); + WriteDebug($"Connecting to WsBaseUrl[{WsBaseUrl}] IshUserName[{_ishUserName}] IshPassword[" + new string('*', ishPasswordLength) + $"] Timeout[{_timeout}] IgnoreSslPolicyErrors[{_ignoreSslPolicyErrors}]"); + IshSession ishSession = new IshSession(Logger, WsBaseUrl, _ishUserName, _ishPassword, _timeout, _ignoreSslPolicyErrors); + + // Keep the IshSession initialization as minimal as possible + // Do early load of IshTypeFieldSetup (either <13-TriDKXmlSetup-based or >=13-RetrieveFieldSetupByIshType-API-based) for + // usage by ToIshMetadataFields/.../ToIshRequestedMetadataFields and Expand-ISHParameter.ps1 parameter autocompletion + //var ishTypeFieldSetup = ishSession.IshTypeFieldSetup; + + WriteObject(true); + } + catch (TrisoftAutomationException trisoftAutomationException) + { + WriteVerbose(trisoftAutomationException.Message); + WriteObject(false); + } + catch (AggregateException aggregateException) + { + var flattenedAggregateException = aggregateException.Flatten(); + WriteVerbose(flattenedAggregateException.ToString()); + WriteObject(false); + } + catch (Exception exception) + { + WriteVerbose(exception.Message); + WriteObject(false); + } + } + } +} diff --git a/Source/ISHRemote/Trisoft.ISHRemote/GlobalSuppressions.cs b/Source/ISHRemote/Trisoft.ISHRemote/GlobalSuppressions.cs index 5569206c..a40d0868 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/GlobalSuppressions.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/GlobalSuppressions.cs @@ -1,6 +1,6 @@ /* * Copyright (c) 2014 All Rights Reserved by the SDL Group. -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/Source/ISHRemote/Trisoft.ISHRemote/ISHRemote.PesterSetup.ps1 b/Source/ISHRemote/Trisoft.ISHRemote/ISHRemote.PesterSetup.ps1 index 2025642f..9026532a 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/ISHRemote.PesterSetup.ps1 +++ b/Source/ISHRemote/Trisoft.ISHRemote/ISHRemote.PesterSetup.ps1 @@ -1,23 +1,32 @@ # # Tests Header dot-sourced import PS1 file for *.Tests.ps1 files # + +# Quick check if you are using Pester 5, or default installed Pester 3.4.0 (#132) +$pesterVersion = [version](Get-Command Invoke-Pester).Version +if ($pesterVersion -lt [version]("5.3.0")) { Write-Warning ("ISHRemote.PesterSetup.ps1 Invoke-Pester version["+$pesterVersion+"] while 5.3+ is expected!") } + $DebugPreference = "SilentlyContinue" # Continue or SilentlyContinue $VerbosePreference = "SilentlyContinue" # Continue or SilentlyContinue $WarningPreference = "Continue" # Continue or SilentlyContinue or Stop $ProgressPreference= "SilentlyContinue" # Continue or SilentlyContinue -Write-Host "Generating and Executing Import-Module Statement..." + +# Generating and Executing Import-Module Statement $project = (Split-Path -Parent $MyInvocation.MyCommand.Path).ToLower() $project = $project.Substring(0, $project.LastIndexOf("ishremote")) $project = ($project + "ishremote") if ($env:GITHUB_ACTIONS -eq "true") { - Import-Module (Join-Path $project "\bin\release\ISHRemote") -DisableNameChecking + $moduleFolder = (Join-Path $project "\bin\release\ISHRemote") } else { - Import-Module (Join-Path $project "\bin\debug\ISHRemote") -DisableNameChecking + $moduleFolder = (Join-Path $project "\bin\debug\ISHRemote") } -Write-Host "Initializing Global Test Data and Variables" +Write-Host ("Running ISHRemote.PesterSetup.ps1 Import Module folder["+$moduleFolder+"] ...") +Import-Module ($moduleFolder) -DisableNameChecking + +Write-Host "Running ISHRemote.PesterSetup.ps1 Global Test Data and Variables initialization" $timestamp = Get-Date -Format "yyyyMMddHHmmss" -Write-Verbose "Initializing OASIS DITA File Contents" +Write-Verbose "Running ISHRemote.PesterSetup.ps1 OASIS DITA File Contents initialization" $ditaTopicFileContent = @" @@ -36,7 +45,7 @@ $ditaMapWithTopicrefFileContent = @" "@ -Write-Verbose "Initializing variables for UserName/Password based tests, so ISHSTS-like..." +Write-Verbose "Running ISHRemote.PesterSetup.ps1 variables for UserName/Password based tests, so ISHSTS-like...initialization" $baseUrl = $env:ISH_BASE_URL if ([string]::IsNullOrEmpty($baseUrl)) { @@ -54,8 +63,9 @@ if ([string]::IsNullOrEmpty($ishPassword)) { $ishPassword = 'admin' } +$webServicesBaseUrl = "$baseUrl/ISHWS/" # must have trailing slash for tests to succeed -Write-Verbose "Initializing variables for System Setup" +Write-Verbose "Running ISHRemote.PesterSetup.ps1 variables for System Setup initialization" $folderTestRootPath = "\General\__ISHRemote" # requires leading FolderPathSeparator for tests to succeed $ishLng = 'VLANGUAGEEN' $ishLngLabel = 'en' @@ -71,9 +81,10 @@ $ishLngCombination = 'en' # LanguageCombination like 'en+fr+nl' can only be exp $ishOutputFormatDitaXml = 'GUID-079A324-FE52-45C4-82CD-A1A9663C2777' # 'DITA XML' element name $ishLovId = "DLANGUAGE" # ListOfValues where the Lov tests will work on $ishLovId2 = "DRESOLUTION" # ListOfValues where the Lov tests will work on +$ishEventTypeToPurge = "PUSHTRANSLATIONS" #region Placeholder to inject your variable overrides. -Write-Host "Initializing Global Test Data and Variables for debug" +Write-Host "Running ISHRemote.PesterSetup.ps1 Global Test Data and Variables for debug initialization" $debugPesterSetupFilePath = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "ISHRemote.PesterSetup.Debug.ps1" if (Test-Path -Path $debugPesterSetupFilePath -PathType Leaf) { @@ -88,24 +99,6 @@ if (Test-Path -Path $debugPesterSetupFilePath -PathType Leaf) } #endregion -$webServicesBaseUrl = "$baseUrl/ISHWS/" # must have trailing slash for tests to succeed -$wsTrustIssuerUrl = "$baseUrl/ISHSTS/issue/wstrust/mixed/username" -$wsTrustIssuerMexUrl = "$baseUrl/ISHSTS/issue/wstrust/mex" - $webServicesBaseUrl -match "https://((?.+))+/(.)+/" | Out-Null $hostname=$Matches['hostname'] $localWebServicesBaseUrl = $webServicesBaseUrl.Replace($hostname,"localhost") -$localWsTrustIssuerUrl = $wsTrustIssuerUrl.Replace($hostname,"localhost") -$localWsTrustIssuerMexUrl = $wsTrustIssuerMexUrl.Replace($hostname,"localhost") - -# -# Note -# * Only variables and generic PowerShell object initialization -# * Avoid ISHRemote execution, $ishSession creation will be overwritten in New-IshSession to avoid tests to fail -# -#if ($null -eq $global:ishSession) -#{ - # $global:ishSession = New-IshSession -WsBaseUrl $webServicesBaseUrl -IshUserName $ishUserName -IshPassword $ishPassword -#} -# $ishSession = $global:ishSession -# TODO [Must] The StateStore is now required for all tests, but it is only done in New-IshSession. 50s performance boost to gain diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs b/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs index acc636ae..738afce5 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs @@ -39,6 +39,7 @@ public class IshSession : IDisposable private IshConnectionConfiguration _ishConnectionConfiguration; private string _ishUserName; private string _userName; + private string _userLanguage; private readonly string _ishPassword; private readonly string _separator = ", "; private readonly string _folderPathSeparator = @"\"; @@ -56,18 +57,17 @@ public class IshSession : IDisposable private Enumerations.PipelineObjectPreference _pipelineObjectPreference = Enumerations.PipelineObjectPreference.PSObjectNoteProperty; private Enumerations.RequestedMetadataGroup _defaultRequestedMetadata = Enumerations.RequestedMetadataGroup.Basic; + /// + /// Used by the SOAP API that retrieves files/blobs in multiple chunk, this parameter is the chunksize (10485760 bytes is 10Mb) + /// private int _chunkSize = 10485760; + /// + /// Used to divide bigger data set retrievals in multiple API calls, 999 is the best optimization server-side (Oracle IN-clause only allows 999 values, so 1000 would mean 2x queries server-side) + /// private int _metadataBatchSize = 999; private int _blobBatchSize = 50; private TimeSpan _timeout = new TimeSpan(0, 0, 20); // up to 15s for a DNS lookup according to https://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout%28v=vs.110%29.aspx - //TODO [Must] ISHRemotev7+ Cleanup// private TimeSpan _timeoutIssue = TimeSpan.MaxValue; - //TODO [Must] ISHRemotev7+ Cleanup// private readonly TimeSpan _timeoutService = TimeSpan.MaxValue; private readonly bool _ignoreSslPolicyErrors = false; - //TODO [Must] ISHRemotev7+ Cleanup// private readonly bool _explicitIssuer = false; - - - - //TODO [Must] ISHRemotev7+ Cleanup// private InfoShareWcfConnection _connection; //private Annotation25ServiceReference.Annotation _annotation25; private Application25ServiceReference.Application25SoapClient _application25; @@ -116,45 +116,6 @@ public IshSession(ILogger logger, string webServicesBaseUrl, string ishUserName, CreateConnection(); } - //TODO [Must] ISHRemotev7+ Cleanup - /* - /// - /// Creates a session object holding contracts and proxies to the web services API. Takes care of username/password and 'Active Directory' authentication (NetworkCredential) to the Secure Token Service. - /// - /// Instance of the ILogger interface to allow some logging although Write-* is not very thread-friendly. - /// The url to the web service API. For example 'https://example.com/ISHWS/' - /// The url to the security token service wstrust endpoint. For example 'https://example.com/ISHSTS/issue/wstrust/mixed/username' - /// The binding for the wsTrustEndpoint url - /// InfoShare user name. For example 'Admin' - /// Matching password as SecureString of the incoming user name. When null is provided, a NetworkCredential() is created instead. - /// Timeout to control Send/Receive timeouts of HttpClient when downloading content like connectionconfiguration.xml - /// Timeout to control Send/Receive timeouts of WCF when issuing a token - /// Timeout to control Send/Receive timeouts of WCF for InfoShareWS proxies - /// IgnoreSslPolicyErrors presence indicates that a custom callback will be assigned to ServicePointManager.ServerCertificateValidationCallback. Defaults false of course, as this is creates security holes! But very handy for Fiddler usage though. - public IshSession(ILogger logger, string webServicesBaseUrl, string wsTrustIssuerUrl, string wsTrustIssuerMexUrl, string ishUserName, SecureString ishSecurePassword, TimeSpan timeout, TimeSpan timeoutIssue, TimeSpan timeoutService, bool ignoreSslPolicyErrors) - { - _logger = logger; - _explicitIssuer = true; - _ignoreSslPolicyErrors = ignoreSslPolicyErrors; - if (_ignoreSslPolicyErrors) - { - CertificateValidationHelper.OverrideCertificateValidation(); - } - ServicePointManagerHelper.RestoreCertificateValidation(); - // webServicesBaseUrl should have trailing slash, otherwise .NET throws unhandy "Reference to undeclared entity 'raquo'." error - _webServicesBaseUri = (webServicesBaseUrl.EndsWith("/")) ? new Uri(webServicesBaseUrl) : new Uri(webServicesBaseUrl + "/"); - _wsTrustIssuerUri = new Uri(wsTrustIssuerUrl); - _wsTrustIssuerMexUri = new Uri(wsTrustIssuerMexUrl); - - _ishUserName = ishUserName == null ? Environment.UserName : ishUserName; - _ishPassword = ishSecurePassword; - _timeout = timeout; - _timeoutIssue = timeoutIssue; - _timeoutService = timeoutService; - CreateConnection(); - } - */ - private void LoadConnectionConfiguration() { var client = new HttpClient(); @@ -164,10 +125,10 @@ private void LoadConnectionConfiguration() var responseMessage = client.GetAsync(connectionConfigurationUri).GetAwaiter().GetResult(); string response = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult(); _ishConnectionConfiguration = new IshConnectionConfiguration(response); - _logger.WriteDebug($"LoadConnectionConfiguration found InfoShareWSUrl[${_ishConnectionConfiguration.InfoShareWSUrl}] ApplicationName[${_ishConnectionConfiguration.ApplicationName}] SoftwareVersion[${_ishConnectionConfiguration.SoftwareVersion}]"); + _logger.WriteDebug($"LoadConnectionConfiguration found InfoShareWSUrl[{_ishConnectionConfiguration.InfoShareWSUrl}] ApplicationName[{_ishConnectionConfiguration.ApplicationName}] SoftwareVersion[{_ishConnectionConfiguration.SoftwareVersion}]"); if (_ishConnectionConfiguration.InfoShareWSUrl != _webServicesBaseUri) { - _logger.WriteDebug($"LoadConnectionConfiguration noticed incoming _webServicesBaseUri[${_webServicesBaseUri}] differs from _ishConnectionConfiguration.InfoShareWSUrl[${_ishConnectionConfiguration.InfoShareWSUrl}]"); + _logger.WriteDebug($"LoadConnectionConfiguration noticed incoming _webServicesBaseUri[{_webServicesBaseUri}] differs from _ishConnectionConfiguration.InfoShareWSUrl[{_ishConnectionConfiguration.InfoShareWSUrl}]. Using _webServicesBaseUri."); } } @@ -277,6 +238,27 @@ public string UserName } } + /// + /// The user language as available on the InfoShare User Profile in the CMS under field 'FISHUSERLANGUAGE' + /// + public string UserLanguage + { + get + { + if (_userLanguage == null) + { + //TODO [Could] IshSession could initialize the current IshUser completely based on all available user metadata and store it on the IshSession + string requestedMetadata = ""; + string xmlIshObjects = ""; + User25.GetMyMetaData(ref _authenticationContext, requestedMetadata, ref xmlIshObjects); + Enumerations.ISHType[] ISHType = { Enumerations.ISHType.ISHUser }; + IshObjects ishObjects = new IshObjects(ISHType, xmlIshObjects); + _userLanguage = ishObjects.Objects[0].IshFields.GetFieldValue("FISHUSERLANGUAGE", Enumerations.Level.None, Enumerations.ValueType.Value); + } + return _userLanguage; + } + } + internal IshVersion ServerIshVersion { get { return _serverVersion; } @@ -346,35 +328,13 @@ public TimeSpan Timeout set { _timeout = value; } } - //TODO [Must] ISHRemotev7+ Cleanup - /* - /// - /// Timeout to control Send/Receive timeouts of WCF when issuing a token - /// - public TimeSpan TimeoutIssue - { - get { return _timeoutIssue; } - set { _timeoutIssue = value; } - } - - - /// - /// Timeout to control Send/Receive timeouts of WCF for InfoShareWS proxies - /// - public TimeSpan TimeoutService - { - get { return _timeoutService; } - // set { _timeoutService = value; } // requires reset of all proxies - } - */ - /// /// Web Service Retrieve batch size, if implemented, expressed in number of Ids/Objects for usage in metadata calls /// public int MetadataBatchSize { get { return _metadataBatchSize; } - set { _metadataBatchSize = value; } + set { _metadataBatchSize = (value > 0) ? value : 999; } } ///