diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ecc64bb7..312de094 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -37,6 +37,9 @@ jobs: echo "GITHUB_RUN_NUMBER[$env:GITHUB_RUN_NUMBER]" echo "GITHUB_RUN_ATTEMPT[$env:GITHUB_RUN_ATTEMPT]" echo "ISHGITHUB_RUN_NUMBER[$env:ISHGITHUB_RUN_NUMBER]" + echo "ISHGITHUB_SERVER_URL[$env:ISHGITHUB_SERVER_URL]" + echo "ISHGITHUB_REPOSITORY[$env:ISHGITHUB_REPOSITORY]" + echo "ISHGITHUB_RUN_ID[$env:ISHGITHUB_RUN_ID]" - uses: actions/checkout@v3 @@ -49,8 +52,6 @@ jobs: run: dotnet restore Source/ISHRemote/ISHRemote.sln - name: Build Solution - env: - NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} run: dotnet build --no-restore --no-incremental --configuration release Source/ISHRemote/ISHRemote.sln - name: Setup PowerShell PSScriptAnalyzer @@ -185,6 +186,29 @@ jobs: path: Cmdlets.Pester.Tests.xml if: ${{ always() }} + - name: "Publish to PowerShellGallery as Prerelease" + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + if: ${{ success() && contains(github.event.head_commit.message, '[PushToPSGalleryAsPreview]') }} + shell: pwsh + run: | + $manifestFilePath = Get-ChildItem "Source/ISHRemote/Trisoft.ISHRemote/bin/Release/ISHRemote/ISHRemote.psd1 + $psModuleInfo = Test-ModuleManifest -Path $manifestFilePath.FullName + $manifestFileVersion = $psModuleInfo.Version + $manifestFileUpdateVersion = $psModuleInfo.PrivateData.PSData['Prerelease'] + $fullVersion = "$manifestFileVersion-$manifestFileUpdateVersion" + $remoteModule = Find-Module -Name ISHRemote -Repository PSGallery -AllowPrerelease + if($remoteModule.Version -ne $fullVersion) + { + echo "Publishing module with version[$fullVersion]!" + # Publish-Module -Path "Source/ISHRemote/Trisoft.ISHRemote/bin/Release/ISHRemote/" -Repository PSGallery -NuGetApiKey $env:NUGET_API_KEY -Verbose -Force -ErrorAction:Continue -WhatIf + } + else + { + echo "Skipping publish of module with version[$fullVersion]!" + } + Publish-Module -path ./jtAz -NuGetApiKey + - name: "Info: How to publish to Internal/Nexus Repositories" shell: pwsh run: | @@ -196,3 +220,4 @@ jobs: echo "3b. Expand-Archive -Path C:\TEMP\ISHRemote\ISHRemote-MainCI-Module.zip -DestinationPath C:\TEMP\ISHRemote\ToPublish\ -Force" echo "3c. Publish-Module -Path C:\TEMP\ISHRemote\ToPublish\ISHRemote.psd1 -Repository $psRepository -NuGetApiKey $nuGetApiKey -Force" echo "3d. Find-Module -Name ISHRemote -Repository $psRepository -AllowPrerelease" + diff --git a/Doc/ReleaseNotes-ISHRemote-8.0.md b/Doc/ReleaseNotes-ISHRemote-8.0.md index 3b9ebbca..5ef6b4bc 100644 --- a/Doc/ReleaseNotes-ISHRemote-8.0.md +++ b/Doc/ReleaseNotes-ISHRemote-8.0.md @@ -25,7 +25,7 @@ Where we used to have only implicit `WcfSoapWithWsTrust` protocol - same as ISHR * Note: ISHWS/OWCF web services have feature parity to ISHWS/WCF (and actually also ISHWS/*.ASMX) * If protocol is forced to `OpenApiWithOpenIdConnect` * You mostly get fully operational WcfSoapWithOpenIdConnect - * You also get an OpenAPI 3.0 experimental proxy on your IShSession object (the future) + * You also get an OpenAPI 3.0 experimental proxy on your IShSession object (experimental, might look different in the future) ### OpenIdConnect Client Credentials Flow @@ -84,7 +84,7 @@ Code, especially around communication and authentication protocol, was heavily r * Renamed `InfoShareWcfSoapConnection.cs` and moved it to `Connection\InfoShareWcfSoapWithWsTrustConnection.cs` * Aligned implementation of new `Connection\InfoShareWcfSoapWithOpenIdConnectConnection.cs` with `Connection\InfoShareWcfSoapWithWsTrustConnection.cs` which should make it easier to extract these `\Connection\` classes if desired. But also removed anything refering to Explicit Issuer (unreachable code since ISHRemote v7.0) an anything regarding `/Internal/` or `/SDL/` realm detection as no longer needed in Tridion Docs 15 (only ISHSTS). -* Introduced _future_ `InfoShareOpenApiWithOpenIdConnectConnection` which offers an NSwag generated proxy to private OpenAPI of Tridion Docs 15.0 Organize Space for experimentation. +* Introduced _ experimental future_ `InfoShareOpenApiWithOpenIdConnectConnection` which offers an NSwag generated proxy to private OpenAPI of Tridion Docs 15.0 Organize Space for experimentation. * Layout of `IshSession` was enriched with `BearerToken` through `ISHRemote.Format.ps1xml`. * Multi-platform code using pragma (e.g. `#if NET48`) for local redirect listener and system browser are * `IshConnectionConfiguration`: Web Service discovery happens over ‘https://ish.example.com/ISHWS/connectionconfiguration.xml’, especially the ServerVersion drives protocol detection and available API functions/behavior. Just like Publication Manager would do. @@ -119,6 +119,8 @@ WARNING: NewIshSession ISHRemote module on PS5.1/NET48 forces Assembly Redirect ## Known Issues +* Aborting the `New-IShSession`/`Test-IShSession` cmdlets using `Ctrl-C` in a PowerShell is not possible, you have to await the non-configurable 60 seconds timeout potentially resulting in `GetTokensOverSystemBrowserAsync Error[Browser login cannceled after 60 seconds.]`. Typically happens if you did not authenticate in the System Browser. +* Refresh Token is not used to refresh the Bearer Token in the background, it is used to refresh when the next cmdlet is triggered before expiration. * On the Github Actions container-based build I received error `Could not load file or assembly 'System.ServiceModel.Primitives, Version=4.10.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.`. This PowerShell 7.2.x issue is seemingly resolved since 7.3.6 as mentioned [here](https://github.com/dotnet/wcf/issues/2862) and has to do with loading .NET Standard libaries in platform libraries (like Trisoft.ISHRemote.dll). Therefor extended the `continuous-integration.yml` to upgrade to PowerShell Preview using [pwshupdater](https://github.com/marketplace/actions/pwshupdater). ## Quality Assurance diff --git a/Doc/TheExecution-ISHRemote-8.0.md b/Doc/TheExecution-ISHRemote-8.0.md index a963add4..fae7bc25 100644 --- a/Doc/TheExecution-ISHRemote-8.0.md +++ b/Doc/TheExecution-ISHRemote-8.0.md @@ -197,11 +197,31 @@ For whoever stumbles on this transitive package dependency of `System.Runtime.Co * GitHub Actions has many issues... had to drop New-ModuleManifest -Prerelease '$(Prerelease)' parameter on PS5.1 and added simple find-replace * Github Actions, preview build number not ever increasing, trying to fix it using `GITHUB_RUN_ATTEMPT` +# Expedite ISHRemote v8 - Must Have Section +* Validate unhappy paths by manual and automated testing. +* Authentication over System Browser, so Authorization Code Flow with Proof Key for Code Exchange (PKCE), will give you 60 seconds. Any slower and you will see the `New-IShSession`/`Test-IShSession` cmdlets respond with `TaskCanceledException` exception stating `Browser login canceled after 60 seconds.` -# Next +* Authentication over Client Credentials Flow with non-existing `-ClientId` will . Please make sure you activate a client/secret on your Access Management User Profile (ISHAM). +* Authentication over Client Credentials Flow with expired `-ClientId`/`-ClientSecret` combination will . Please recycle expired client/secret on your Access Management User Profile (ISHAM). +* Authentication over Client Credentials Flow with valid `-ClientId`/`-ClientSecret` combination, but not mapped in the CMS to a User Profile over `FISHEXTERNALID` will . Please make sure that the client (which you can find on the Access Management User Profile) is added in Organize Space on one CMS User Profile in the comma-seperated External Id field. +* Authentication over Client Credentials Flow with valid `-ClientId`/`-ClientSecret` combination, and mapped in the CMS to a User Profile over `FISHEXTERNALID` which is disabled will . Please make sure in Organize Space that the one CMS User Profile holding the client in the External Id field is an enabled profile. +* Authentication over either Client Credentials or System Browser was succesful but the Bearer Token expired, the Refresh . Please create a `New-IShSession`. + +* Strongly wondering to roll back from Task.Run to simply GetAwaiter().GetResult() as the latter allows loggin to happen! + +* Help + * $ishSessionA = New-IshSession -WsBaseUrl "https://example.com/ISHWSPROD/" -PSCredential "Admin" --> `-PSCredential Admin` only works for `-Protocol WcfSoapWithWsTrust` so it is an outdated sample ... all New-IshSession should be reviewed. + +* Known Issues + * Refresh Token is not used to refresh the Bearer Token in the background, it is used to refresh when the next cmdlet is triggered before expiration. (already in release notes, can be removed or needs updating) + + +# Next - Should Have Section * Test refresh with short expiration * $ishSession.OpenApiISH30Service.GetApplicationVersionAsync() results in `You cannot call a method on a null-valued expression.` * Get-IshVersion (over WcfSoapWithOpenIdConnect) results in `The HTTP status code of the response was not expected (401).` + + * Extend perequisites test regarding client I'd and secret, an expired and valid set... Perhaps over isham20proxy * User provisioning, see [SRQ-23306] Last login date in user overview is not updated when authentication was done through an external identity provider - RWS Jira https://jira.sdl.com/browse/SRQ-23306 * Automated Test ps5.1 with wstrust, ps7 with both openidconnect @@ -209,7 +229,8 @@ For whoever stumbles on this transitive package dependency of `System.Runtime.Co * Once branch #152 is merged, update ticket https://github.com/IdentityModel/Documentation/issues/13 with a hint to `AppDomainModuleAssemblyInitializer.cs` > Took me a while to find this nugget to resolve my problem. It is unfortunate that `OidcClient` doesn't work without these assemblyBinding redirects. For people who have this issue but do not have access to a `.config` file like I had with `powershell.exe.config` (v5.1 on .NET 4.8) - have a look at `AppDomainModuleAssemblyInitializer.cs` on https://github.com/RWS/ISHRemote/ > Another hint is adding `LogSerializer.Enabled = false;` because if you do not attach logging to OidcClient, there seemingly is a bug that still does logging although not configured. see https://github.com/IdentityModel/IdentityModel.OidcClient/pull/67 -* Describe what Tridion Docs User Profile disable means, and when it kicks in. +* OpenAPI, add wires for streamed downloading of Get-IshPublicationOutputData (15.0.0 only, measure performance difference) +* OpenAPI, add wires for Folder cmdlets # Future diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionBase.cs b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionBase.cs index 510ffe4d..440ec950 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionBase.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionBase.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2014 All Rights Reserved by the SDL Group. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -127,7 +127,7 @@ protected async Task GetTokensOverSystemBrowserAsy { _logger.WriteDebug($"GetTokensOverSystemBrowserAsync from Authority[{_connectionParameters.IssuerUrl.ToString()}] using ClientAppId[{_connectionParameters.ClientAppId}] Scope[{_connectionParameters.Scope}]"); - var browser = new InfoShareOpenIdConnectSystemBrowser(_logger, _connectionParameters.RedirectUri); + var browser = new InfoShareOpenIdConnectSystemBrowser(_logger, _connectionParameters.RedirectUri, _connectionParameters.SystemBrowserTimeout); string redirectUri = string.Format($"http://127.0.0.1:{browser.Port}"); diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionParameters.cs b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionParameters.cs index ac896452..f9589265 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionParameters.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectConnectionParameters.cs @@ -107,6 +107,10 @@ public Uri IssuerUrl /// public TimeSpan ServiceTimeout { get { return Timeout; } } /// + /// Timeout to control the wait of the interactive system browser localhost redirect flow + /// + public TimeSpan SystemBrowserTimeout { get; set; } + /// /// If True, certificate validation for HTTPS and the Service will be skipped /// public bool IgnoreSslPolicyErrors { get; set; } = false; diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectLocalHttpEndpoint.cs b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectLocalHttpEndpoint.cs index ecf67006..62a172f7 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectLocalHttpEndpoint.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectLocalHttpEndpoint.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2014 All Rights Reserved by the SDL Group. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -77,7 +77,7 @@ public Task WaitForCallbackAsync(int timeoutInSeconds = 300, Cancellatio countTimeoutInMilliseconds += 100; if (countTimeoutInMilliseconds > timeoutInSeconds * 1000) { - throw new TaskCanceledException($"Browser login cannceled after {timeoutInSeconds} seconds."); + throw new TaskCanceledException($"Browser login canceled after {timeoutInSeconds} seconds."); } Thread.Sleep(100); cancellationToken.ThrowIfCancellationRequested(); @@ -170,4 +170,4 @@ private static int GetFreePort() } -} \ No newline at end of file +} diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectSystemBrowser.cs b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectSystemBrowser.cs index 44d2824d..cb47fc32 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectSystemBrowser.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/Connection/InfoShareOpenIdConnectSystemBrowser.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2014 All Rights Reserved by the SDL Group. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,14 +37,16 @@ public class InfoShareOpenIdConnectSystemBrowser : IBrowser /// Logger /// private readonly ILogger _logger; - public string RedirectUrl = "https://www.rws.com"; + public string RedirectUrl = "https://www.rws.com"; + public TimeSpan SystemBrowserTimeout = new TimeSpan(0,1,30); public int Port { get; } private readonly string _path; - public InfoShareOpenIdConnectSystemBrowser(ILogger logger, string redirectUrl, int? port = null, string path = null) + public InfoShareOpenIdConnectSystemBrowser(ILogger logger, string redirectUrl, TimeSpan systemBrowserTimeout, int? port = null, string path = null) { _logger = logger; RedirectUrl = redirectUrl; + SystemBrowserTimeout = systemBrowserTimeout; _path = path; if (!port.HasValue) @@ -68,15 +70,14 @@ private int GetRandomUnusedPort() public async Task InvokeAsync(BrowserOptions options, CancellationToken cancellationToken) { - int timeoutInSeconds = 90; - _logger.WriteDebug($"InfoShareOpenIdConnectSystemBrowser InvokeAsync port[{Port}] path[{_path}] timeoutInSeconds[{timeoutInSeconds}]"); + _logger.WriteDebug($"InfoShareOpenIdConnectSystemBrowser InvokeAsync port[{Port}] path[{_path}] systemBrowserTimeout[{SystemBrowserTimeout}]"); using (var listener = new InfoShareOpenIdConnectLocalHttpEndpoint(Port, _path)) { OpenBrowser(options.StartUrl); try { - var result = await listener.WaitForCallbackAsync(timeoutInSeconds); + var result = await listener.WaitForCallbackAsync(Convert.ToInt32(SystemBrowserTimeout.TotalSeconds)); _logger.WriteDebug($"InfoShareOpenIdConnectSystemBrowser SendHttpRedirectAsync RedirectUrl[{RedirectUrl}]"); await listener.SendHttpRedirectAsync(RedirectUrl, cancellationToken); @@ -135,4 +136,4 @@ public void OpenBrowser(string url) } } } -} \ No newline at end of file +} diff --git a/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs b/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs index 59f2b3a8..0574057b 100644 --- a/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs +++ b/Source/ISHRemote/Trisoft.ISHRemote/Objects/Public/IshSession.cs @@ -204,6 +204,7 @@ public IshSession(ILogger logger, string webServicesBaseUrl, string ishUserName, InfoShareWSUrl = owcfConnectionConfiguration.InfoShareWSUrl, IssuerUrl = owcfConnectionConfiguration.IssuerUrl, Timeout = _timeout, + SystemBrowserTimeout = new TimeSpan(0, 0, 10),//new TimeSpan(0, 1, 0), ClientAppId = _clientAppId, ClientId = _clientId, ClientSecret = SecureStringConversions.SecureStringToString(_clientSecureSecret) @@ -218,6 +219,7 @@ public IshSession(ILogger logger, string webServicesBaseUrl, string ishUserName, InfoShareWSUrl = owcfConnectionConfiguration.InfoShareWSUrl, IssuerUrl = owcfConnectionConfiguration.IssuerUrl, Timeout = _timeout, + SystemBrowserTimeout = new TimeSpan(0, 0, 10),//new TimeSpan(0, 1, 0), ClientAppId = _clientAppId, ClientId = _clientId, ClientSecret = SecureStringConversions.SecureStringToString(_clientSecureSecret)