Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Doc/ReleaseNotes-ISHRemote-8.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The below text describes the delta compared to fielded release ISHRemote v8.2.
* Migrated all 58 `*.Tests.ps1` files from Pester v5 to Pester v6 (`Should -Be` to `Should-Be`, `Should -BeExactly` to `Should-BeString -CaseSensitive`, `Should -Not -BeNullOrEmpty` to `Should-NotBeNull`, `Should -Throw "msg"` to `Should-Throw -ExceptionMessage "msg"`, etc.). CI install gates updated to `-MinimumVersion 6.0.0`. Classic `Should -Not -Throw` retained as there is no `Should-NotThrow` equivalent in Pester 6. Hardened the library for parallel test execution by replacing the process-wide `TrisoftCmdletLogger` singleton with per-cmdlet `ILogger` routing and adding a double-checked lock on `IshSession._ishTypeFieldSetup` to eliminate Collection was modified races under `Run.Parallel = $true`. CI Pester invocations now use `New-PesterConfiguration` (with `Run.Parallel = $false`) so parallel mode can be toggled in one place when ready. See #242, #265, #266.
* Fixed `New-IshSession` (protocol `WcfSoapWithOpenIdConnect`, PowerShell 7.2+/.NET 6.0+) throwing `FileLoadException: Could not load file or assembly 'Microsoft.IdentityModel.Tokens, Version=8.14.0.0, ...'. The located assembly's manifest definition does not match the assembly reference.` on machines where a different build of `Microsoft.IdentityModel.Tokens` (and related `Duende.IdentityModel.OidcClient`) is registered in the Global Assembly Cache (GAC) — observed on machines with Microsoft Intune Management Extension installed. `AppDomainModuleAssemblyInitializer` now force-loads ISHRemote's own bundled copies of `Duende.IdentityModel`, `Duende.IdentityModel.OidcClient`, `Microsoft.IdentityModel.Abstractions/.Logging/.Tokens/.Tokens.Saml/.Xml` as early as possible during module import, and `SessionCmdlet.BeginProcessing` now reports the full forced list over `-Verbose`. Root cause for the `Duende.IdentityModel.OidcClient` variant: `InfoShareOpenIdConnectSystemBrowser` was `public` and implemented `Duende.IdentityModel.OidcClient.Browser.IBrowser`, which put it in `Trisoft.ISHRemote.dll`'s exported types, forcing PowerShell's own binary-module cmdlet discovery (`Assembly.GetExportedTypes()`) to resolve `Duende.IdentityModel.OidcClient` before `IModuleAssemblyInitializer.OnImport()` ever ran — see Breaking Changes - Code. A new `TestPrerequisite.Tests.ps1` check asserts no assembly is ever loaded from the GAC on PowerShell Core. See #272. Thanks @ddemeyer
* Fixed cmdlets over protocol `WcfSoapWithOpenIdConnect` occasionally throwing `An unsecured or incorrectly secured fault was received from the other party` on the first SOAP call after a channel fault, requiring the user to re-run the same cmdlet for it to succeed (the existing #201/#219 rebuild-on-next-call logic only kicked in on a second, separate call). Each `Get*25Channel()` method in `InfoShareWcfSoapWithOpenIdConnectConnection` now returns the channel wrapped in a new `RetryOnFaultProxy<T>` (`System.Reflection.DispatchProxy`) that catches `CommunicationException`/`FaultException` on the actual SOAP call, rebuilds the channel via the existing rebuild logic, and retries exactly once within the same cmdlet invocation before propagating any further failure — with the original exception type/stack trace preserved. Requires a new `net48`-only NuGet dependency, `System.Reflection.DispatchProxy` (built into the BCL on `net6.0`/`net10.0`). See #273. Thanks @ddemeyer
* Enhanced `New-IshSession` and `Test-IshSession` protocol `Autodetect` (the default) to select `OpenApiWithOpenIdConnect` on Tridion Docs 15/15.0.0 and later, instead of `WcfSoapWithOpenIdConnect` on earlier ISHRemote versions. Cmdlets already choose between OpenAPI and SOAP per call, so `WcfSoapWithOpenIdConnect` remains the per-cmdlet fallback. An explicit `-Protocol`, including `WcfSoapWithOpenIdConnect`, works as before. See #282. Thanks @ddemeyer
* Package bumps, deliberate adding of `System.Security.Cryptography.Xml` to fix vulnerability coming from transitive `System.ServiceModel.Primitives`. See #278 and #279. Thanks @ddemeyer


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,8 @@ Describe "New-IshSession" -Tags "Read" {
Context "New-IshSession over OpenApiWithOpenIdConnect returns IshSession object" {
BeforeAll {
if (([Version]$ishSession.ServerVersion).Major -ge 15) { # new service since 15/15.0.0
$localIShSession = New-IshSession -Protocol OpenApiWithOpenIdConnect -WsBaseUrl $webServicesBaseUrl -ClientId $amClientId -ClientSecret $amClientSecret
# no -Protocol on purpose, so Protocol Autodetect must select OpenApiWithOpenIdConnect on 15/15.0.0 and later
$localIShSession = New-IshSession -WsBaseUrl $webServicesBaseUrl -ClientId $amClientId -ClientSecret $amClientSecret
}
}
It "Local ISHRemoteSessionStateIshSession" {
Expand Down Expand Up @@ -890,6 +891,10 @@ Describe "New-IshSession" -Tags "Read" {
$openApiAM10ClientMembers20251210 = @( 'ApiResources_GetAsync','ApiResources_GetByIdAsync','Applications_CreateAsync','Applications_DeleteAsync','Applications_GetAsync','Applications_GetByIdAsync','Applications_UpdateAsync','AuditLogs_GetByFilterAsync','AuditLogs_GetByIdAsync','Equals','GetHashCode','GetType','IdentityProviders_CreateAsync','IdentityProviders_DeleteAsync','IdentityProviders_GetAsync','IdentityProviders_GetAvailableIdpTypesAsync','IdentityProviders_GetByIdAsync','IdentityProviders_GetIconAsync','IdentityProviders_GetLoginOptionsAsync','IdentityProviders_GetParametersForIdentityProviderTypeAsync','IdentityProviders_UpdateAsync','ServiceAccounts_CreateAsync','ServiceAccounts_DeleteAsync','ServiceAccounts_DeleteClientSecretAsync','ServiceAccounts_GenerateClientSecretAsync','ServiceAccounts_GetAsync','ServiceAccounts_GetByIdAsync','ServiceAccounts_UpdateAsync','ServiceAccounts_UpdateClientSecretAsync','System_GetLoginStatusAsync','System_GetSupportedCulturesAsync','System_GetUserInfoAsync','ToString','Users_ActivateUserAsync','Users_DeactivateUserAsync','Users_DeleteAsync','Users_DeleteClientSecretAsync','Users_GenerateClientSecretAsync','Users_GetAsync','Users_GetByIdAsync','Users_UpdateAsync','Users_UpdateClientSecretAsync' ) # Since v8.2
# Up to ISHRemote v8.2 $ishSession.OpenApiAM10Client was CamelCase, chose to break and align to Snake_Case as delivered by Access Management since v2.0.0 so Tridion Docs 15.0.0
$openApiAM10ClientMembers20240404 = $openApiAM10ClientMembers20251210
# Method names are retrieved once, as repeated PSObject.Methods.Name reflection per member made the OperationIds tests very slow
$openApiISH30ClientActualMembers = @($localIShSession.OpenApiISH30Client.PSObject.Methods.Name)
$openApiAM10ClientActualMembers = @($localIShSession.OpenApiAM10Client.PSObject.Methods.Name)
$openApiClientIgnoredMembers = @('Equals','GetHashCode','GetType','ToString','get_BaseUrl','set_BaseUrl','get_ReadResponseAsString','set_ReadResponseAsString')
}
}
It "IshSession.OpenApiISH30Client" {
Expand All @@ -900,25 +905,20 @@ Describe "New-IshSession" -Tags "Read" {
}
It "IshSession.OpenApiISH30Client OperationIds (15.1 20240404 methods exists)" {
if (([Version]$ishSession.ServerVersion).Major -ge 15) { # new service since 15/15.0.0
foreach ($member in $openApiISH30ClientMembers20240404) {
$localIShSession.OpenApiISH30Client.PSObject.Methods.Name | Should-ContainCollection $member
}
$missingMembers = @($openApiISH30ClientMembers20240404 | Where-Object { $_ -notin $openApiISH30ClientActualMembers })
("missing[" + ($missingMembers -join ',') + "]") | Should-BeString "missing[]"
}
}
It "IshSession.OpenApiISH30Client OperationIds (15.3 20251210 methods exists)" {
if ((([Version]$ishSession.ServerVersion).Major -ge 15) -and (([Version]$ishSession.ServerVersion).Minor -ge 2)) { # new service since 15.2/15.2.0
foreach ($member in $openApiISH30ClientMembers20251210) {
$localIShSession.OpenApiISH30Client.PSObject.Methods.Name | Should-ContainCollection $member
}
if ([Version]$ishSession.ServerVersion -ge [Version]"15.2") { # new service since 15.2/15.2.0
$missingMembers = @($openApiISH30ClientMembers20251210 | Where-Object { $_ -notin $openApiISH30ClientActualMembers })
("missing[" + ($missingMembers -join ',') + "]") | Should-BeString "missing[]"
}
}
It "IshSession.OpenApiISH30Client OperationIds (new methods found, adapt this test)" {
if (([Version]$ishSession.ServerVersion).Major -ge 15) { # new service since 15/15.0.0
foreach ($member in $localIShSession.OpenApiISH30Client.PSObject.Methods.Name) {
if ($member -notin @('Equals','GetHashCode','GetType','ToString','get_BaseUrl','set_BaseUrl','get_ReadResponseAsString','set_ReadResponseAsString')) {
$openApiISH30ClientMembers20251210 | Should-ContainCollection $member
}
}
$superfluousMembers = @($openApiISH30ClientActualMembers | Where-Object { ($_ -notin $openApiClientIgnoredMembers) -and ($_ -notin $openApiISH30ClientMembers20251210) })
("superfluous[" + ($superfluousMembers -join ',') + "]") | Should-BeString "superfluous[]"
}
}
It "IshSession.OpenApiAM10Client" {
Expand All @@ -928,16 +928,12 @@ Describe "New-IshSession" -Tags "Read" {
}
}
It "IshSession.OpenApiAM10Client OperationIds (20240404 methods exists)" {
foreach ($member in $openApiAM10ClientMembers20240404) {
$localIShSession.OpenApiAM10Client.PSObject.Methods.Name | Should-ContainCollection $member
}
$missingMembers = @($openApiAM10ClientMembers20240404 | Where-Object { $_ -notin $openApiAM10ClientActualMembers })
("missing[" + ($missingMembers -join ',') + "]") | Should-BeString "missing[]"
}
It "IshSession.OpenApiAM10Client OperationIds (new methods found, adapt this test)" {
foreach ($member in $localIShSession.OpenApiAM10Client.PSObject.Methods.Name) {
if ($member -notin @('Equals','GetHashCode','GetType','ToString','get_BaseUrl','set_BaseUrl','get_ReadResponseAsString','set_ReadResponseAsString')) {
$openApiAM10ClientMembers20240404 | Should-ContainCollection $member
}
}
$superfluousMembers = @($openApiAM10ClientActualMembers | Where-Object { ($_ -notin $openApiClientIgnoredMembers) -and ($_ -notin $openApiAM10ClientMembers20240404) })
("superfluous[" + ($superfluousMembers -join ',') + "]") | Should-BeString "superfluous[]"
}
It "IshSession.Annotation25" {
if (([Version]$ishSession.ServerVersion).Major -ge 15) { # new service since 15/15.0.0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2014 All Rights Reserved by the SDL Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -29,7 +29,7 @@ namespace Trisoft.ISHRemote.Cmdlets.Session
{
/// <summary>
/// <para type="synopsis">The New-IshSession cmdlet creates a new IshSession object using the parameters that are provided.</para>
/// <para type="description">The communication protocol will be derived from the server-side product version. On 14SP4/14.0.4 plus ISHRemote 7.0 and earlier there is only WcfSoapWithWsTrust. Since 15/15.0.0 ISHRemote prefers Modern Authentication and selects WcfSoapWithOpenIdConnect. Experimental since 15/15.0.0 is OpenApiWithOpenIdConnect, where already possible cmdlets will go over OpenAPI REST calls instead of WcfSoapWithOpenIdConnect calls.</para>
/// <para type="description">The communication protocol will be derived from the server-side product version. On 14SP4/14.0.4 plus ISHRemote 7.0 and earlier there is only WcfSoapWithWsTrust. Since 15/15.0.0 ISHRemote prefers Modern Authentication and selects OpenApiWithOpenIdConnect, where cmdlets with an OpenAPI implementation go over OpenAPI REST calls and all other cmdlets fall back to WcfSoapWithOpenIdConnect calls.</para>
/// <para type="description">The New-IshSession cmdlet creates a new IshSession object for usage on any other cmdlet later using the parameters that are provided.</para>
/// <para type="description">The object contains the service endpoint proxies, and api contract information like multi-value separator, date format, etc</para>
/// </summary>
Expand All @@ -46,7 +46,7 @@ namespace Trisoft.ISHRemote.Cmdlets.Session
/// </code>
/// <para>Building a session for the chosen service based on implicit authentication.</para>
/// <para>Protocol will be WcfSoapWithWsTrust on 14SP4/14.0.4 and earlier systems. An implicit NetworkCredential object will be passed for authentication to the service on PowerShell 5.1 - will throw an error on PowerShell 7.2+.</para>
/// <para>Protocol will be WcfSoapWithOpenIdConnect on 15/15.0.0 and later systems. This will trigger the interactive system browser based flow - so your browser as trusted single sign on client.</para>
/// <para>Protocol will be OpenApiWithOpenIdConnect on 15/15.0.0 and later systems. This will trigger the interactive system browser based flow - so your browser as trusted single sign on client.</para>
/// </example>
/// <example>
/// <code>
Expand All @@ -60,7 +60,7 @@ namespace Trisoft.ISHRemote.Cmdlets.Session
/// $ishSession = New-IshSession -WsBaseUrl "https://example.com/ISHWS/" -PSCredential "Admin"
/// </code>
/// <para>Protocol will be WcfSoapWithWsTrust on 14SP4/14.0.4 and earlier systems. Iteratively the New-IshSession line with PSCredential parameter holding a string representation will prompt you for a password.</para>
/// <para>Protocol will be WcfSoapWithOpenIdConnect on 15/15.0.0 and later systems. Iteratively the New-IshSession line with PSCredential parameter holding a string representation will prompt you for a ClientSecret. Note that sample 'Admin' as ClientId has to be configured in Access Management (ISHAM), by default ClientId typically is a GUID.</para>
/// <para>Protocol will be OpenApiWithOpenIdConnect on 15/15.0.0 and later systems. Iteratively the New-IshSession line with PSCredential parameter holding a string representation will prompt you for a ClientSecret. Note that sample 'Admin' as ClientId has to be configured in Access Management (ISHAM), by default ClientId typically is a GUID.</para>
/// </example>
/// <example>
/// <code>
Expand All @@ -77,14 +77,14 @@ namespace Trisoft.ISHRemote.Cmdlets.Session
/// </code>
/// <para>Extensive automation example based on the PSCredential parameter. Responsibility of the plain text password or client secret is yours. Check Microsoft module https://devblogs.microsoft.com/powershell/secretmanagement-and-secretstore-are-generally-available/</para>
/// <para>Protocol will be WcfSoapWithWsTrust on 14SP4/14.0.4 and earlier systems.</para>
/// <para>Protocol will be WcfSoapWithOpenIdConnect on 15/15.0.0 and later systems. Note that sample 'MYISHUSERNAME' as ClientId has to be configured in Access Management (ISHAM), by default ClientId typically is a GUID.</para>
/// <para>Protocol will be OpenApiWithOpenIdConnect on 15/15.0.0 and later systems. Note that sample 'MYISHUSERNAME' as ClientId has to be configured in Access Management (ISHAM), by default ClientId typically is a GUID.</para>
/// </example>
/// <example>
/// <code>
/// $ishSession = New-IshSession -WsBaseUrl "https://example.com/ISHWS/" -ClientId "c826e7e1-c35c-43fe-9756-e1b61f44bb40" -ClientSecret "ziKiGbx6N0G3m69/vWMZUTs2paVO1Mzqt6Y6TX7mnpPJyFVODsI1Vw=="
/// </code>
/// <para>Building a session for the chosen service based on Client Credentials authentication.</para>
/// <para>Protocol will be WcfSoapWithOpenIdConnect on 15/15.0.0 and later systems. Note that ClientId/ClientSecret has to be configured in Access Management (ISHAM), by default ClientId typically is a GUID.</para>
/// <para>Protocol will be OpenApiWithOpenIdConnect on 15/15.0.0 and later systems. Note that ClientId/ClientSecret has to be configured in Access Management (ISHAM), by default ClientId typically is a GUID.</para>
/// </example>
/// <example>
/// <code>
Expand Down Expand Up @@ -184,7 +184,7 @@ public SwitchParameter IgnoreSslPolicyErrors
}

/// <summary>
/// <para type="description">IshSession Protocol explicitly selects the communication protocol; either WcfSoapWithWsTrust (legacy option of Tridion Docs 14SP4/14.0.4, in turn ISHRemote 7.0 and earlier), WcfSoapWithOpenIdConnect (since Tridion Docs 15/15.0.0) and OpenApiWithOpenIdConnect (experimental since Tridion Docs 15/15.0.0). If not provided, the CMS version will pick the most optimal support protocol. See also <see cref="Protocol"/>.</para>
/// <para type="description">IshSession Protocol explicitly selects the communication protocol; either WcfSoapWithWsTrust (legacy option of Tridion Docs 14SP4/14.0.4, in turn ISHRemote 7.0 and earlier), WcfSoapWithOpenIdConnect (since Tridion Docs 15/15.0.0) and OpenApiWithOpenIdConnect (since Tridion Docs 15/15.0.0). If not provided, or default value Autodetect, WcfSoapWithWsTrust is picked on Tridion Docs 14SP4/14.0.4 and earlier, and OpenApiWithOpenIdConnect (OpenAPI where available, WcfSoapWithOpenIdConnect as per-cmdlet fallback) since Tridion Docs 15/15.0.0. See also <see cref="Protocol"/>.</para>
/// </summary>
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, ParameterSetName = "Interactive")]
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, ParameterSetName = "UserNamePassword")]
Expand Down
Loading
Loading