Skip to content

Commit

Permalink
Github Actions, Publishing to PSGallery with Preview build, attempt 0…
Browse files Browse the repository at this point in the history
…1... Next Fix token expiration ... Later deep dive into folder cmdlets based on OpenApi
  • Loading branch information
ddemeyer committed Oct 16, 2023
1 parent 018d4eb commit a26e020
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 18 deletions.
29 changes: 27 additions & 2 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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: |
Expand All @@ -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"
6 changes: 4 additions & 2 deletions Doc/ReleaseNotes-ISHRemote-8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
25 changes: 23 additions & 2 deletions Doc/TheExecution-ISHRemote-8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,19 +197,40 @@ 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
* Test all protocol types on all platforms via newishsession (and one other smoke test) by calling it 6 times (2 ps times 3 protocols) which colors right after prerequisites
* 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
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 @@ -127,7 +127,7 @@ protected async Task<InfoShareOpenIdConnectTokens> 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}");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ public Uri IssuerUrl
/// </summary>
public TimeSpan ServiceTimeout { get { return Timeout; } }
/// <summary>
/// Timeout to control the wait of the interactive system browser localhost redirect flow
/// </summary>
public TimeSpan SystemBrowserTimeout { get; set; }
/// <summary>
/// If True, certificate validation for HTTPS and the Service will be skipped
/// </summary>
public bool IgnoreSslPolicyErrors { get; set; } = false;
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 @@ -77,7 +77,7 @@ public Task<string> 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();
Expand Down Expand Up @@ -170,4 +170,4 @@ private static int GetFreePort()


}
}
}
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 @@ -37,14 +37,16 @@ public class InfoShareOpenIdConnectSystemBrowser : IBrowser
/// Logger
/// </summary>
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)
Expand All @@ -68,15 +70,14 @@ private int GetRandomUnusedPort()

public async Task<BrowserResult> 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);
Expand Down Expand Up @@ -135,4 +136,4 @@ public void OpenBrowser(string url)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down

0 comments on commit a26e020

Please sign in to comment.