From fcc88a4d392e7856ce8b27e39fc985b901b3e13b Mon Sep 17 00:00:00 2001 From: Freddy Kristiansen Date: Fri, 31 May 2024 23:16:52 +0200 Subject: [PATCH] generateDependencyArtifact doesn't work when useCompilerFolder is true (#3564) Fixes https://github.com/microsoft/AL-Go/issues/1093 Support for federated credentials in New-BcAuthContext --------- Co-authored-by: freddydk --- AppHandling/Run-AlPipeline.ps1 | 15 ++++- Auth/New-BcAuthContext.ps1 | 109 ++++++++++++++++++++------------- Auth/Renew-BcAuthContext.ps1 | 1 + ReleaseNotes.txt | 2 + 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/AppHandling/Run-AlPipeline.ps1 b/AppHandling/Run-AlPipeline.ps1 index 12cdb3424..4a8600c69 100644 --- a/AppHandling/Run-AlPipeline.ps1 +++ b/AppHandling/Run-AlPipeline.ps1 @@ -1183,6 +1183,7 @@ Measure-Command { Write-Host -ForegroundColor Yellow "Installing apps for additional country $testCountry" } + $dependenciesFolder = Join-Path $buildArtifactFolder "Dependencies" $tmpAppFolder = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid().ToString()) $tmpAppFiles = @() $installApps | ForEach-Object{ @@ -1205,9 +1206,17 @@ Measure-Command { Invoke-Command -ScriptBlock $InstallBcAppFromAppSource -ArgumentList $Parameters } } - elseif ($useCompilerFolder -or $filesOnly -and (-not $bcAuthContext)) { + elseif (!$testCountry -and ($useCompilerFolder -or ($filesOnly -and (-not $bcAuthContext)))) { CopyAppFilesToFolder -appfiles $_ -folder $packagesFolder | ForEach-Object { - Write-Host "Copying $($_.SubString($packagesFolder.Length+1)) to symbols folder" + Write-Host -NoNewline "Copying $($_.SubString($packagesFolder.Length+1)) to symbols folder" + if ($generateDependencyArtifact) { + Write-Host -NoNewline " and dependencies folder" + if (!(Test-Path $dependenciesFolder)) { + New-Item -ItemType Directory -Path $dependenciesFolder | Out-Null + } + Copy-Item -Path $_ -Destination $dependenciesFolder -Force + } + Write-Host } } else { @@ -1232,7 +1241,7 @@ Measure-Command { } if ($generateDependencyArtifact -and !($testCountry)) { $parameters += @{ - "CopyInstalledAppsToFolder" = Join-Path $buildArtifactFolder "Dependencies" + "CopyInstalledAppsToFolder" = $dependenciesFolder } } if ($bcAuthContext) { diff --git a/Auth/New-BcAuthContext.ps1 b/Auth/New-BcAuthContext.ps1 index 6f734f75d..6f1e8ecb4 100644 --- a/Auth/New-BcAuthContext.ps1 +++ b/Auth/New-BcAuthContext.ps1 @@ -20,6 +20,8 @@ If Refresh token is specified, the refresh_token flow will be included in the list of OAuth2 flows to try .Parameter clientSecret If ClientSecret is specified, the client_credentials flow will be included in the list of OAuth2 flows to try + .Parameter clientAssertion + If ClientAssertion is specified, the client_credentials flow (with ClientAssertion instead of ClientSecret) will be included in the list of OAuth2 flows to try .Parameter credential If Credential is specified, the password flow will be included in the list of OAuth2 flows to try .Parameter includeDeviceLogin @@ -46,6 +48,7 @@ function New-BcAuthContext { [string] $refreshToken, [string] $scopes = "$($bcContainerHelperConfig.apiBaseUrl.TrimEnd('/'))/", $clientSecret, + $clientAssertion, [PSCredential] $credential, [switch] $includeDeviceLogin, [Timespan] $deviceLoginTimeout = [TimeSpan]::FromMinutes(15), @@ -59,6 +62,9 @@ try { if ($clientSecret -and ($clientSecret -isnot [SecureString])) { $clientSecret = ConvertTo-SecureString -String "$clientSecret" -AsPlainText -Force } + if ($clientAssertion -and ($clientAssertion -isnot [SecureString])) { + $clientAssertion = ConvertTo-SecureString -String "$clientAssertion" -AsPlainText -Force + } if ($deviceCode) { $includeDeviceLogin = $true @@ -79,7 +85,7 @@ try { "deviceCode" = $deviceCode } $accessToken = $null - if ($clientSecret) { + if ($clientSecret -or $clientAssertion) { if ($scopes.EndsWith('/')) { $scopes += ".default" } @@ -91,10 +97,20 @@ try { "grant_type" = "client_credentials" "scope" = $scopes "client_id" = $clientId - "client_secret" = ($clientSecret | Get-PlainText) } Headers = @{ "Content-Type" = "application/x-www-form-urlencoded" } } + if ($clientSecret) { + $TokenRequestParams.Body += @{ + "client_secret" = ($clientSecret | Get-PlainText) + } + } + else { + $TokenRequestParams.Body += @{ + "client_assertion_type" = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' + "client_assertion" = ($clientAssertion | Get-PlainText) + } + } try { if (!$silent) { Write-Host "Attempting authentication to $scopes using clientCredentials..." } $TokenRequest = Invoke-RestMethod @TokenRequestParams -UseBasicParsing @@ -110,14 +126,15 @@ try { } $authContext += @{ - "AccessToken" = $accessToken - "UtcExpiresOn" = $expiresOn - "RefreshToken" = $null - "Credential" = $null - "ClientSecret" = $clientSecret - "appid" = $jwtToken.appid - "name" = "" - "upn" = "" + "AccessToken" = $accessToken + "UtcExpiresOn" = $expiresOn + "RefreshToken" = $null + "Credential" = $null + "ClientSecret" = $clientSecret + "ClientAssertion" = $clientAssertion + "appid" = $jwtToken.appid + "name" = "" + "upn" = "" } if ($tenantID -eq "Common") { if (!$silent) { Write-Host "Authenticated to common, using tenant id $($jwtToken.tid)" } @@ -155,14 +172,15 @@ try { $jwtToken = Parse-JWTtoken -token $accessToken if (!$silent) { Write-Host -ForegroundColor Green "Authenticated from $($jwtToken.ipaddr) as user $($jwtToken.name) ($($jwtToken.unique_name))" } $authContext += @{ - "AccessToken" = $accessToken - "UtcExpiresOn" = [Datetime]::UtcNow.AddSeconds($TokenRequest.expires_in) - "RefreshToken" = $null - "Credential" = $credential - "ClientSecret" = $null - "appid" = "" - "name" = $jwtToken.name - "upn" = $jwtToken.unique_name + "AccessToken" = $accessToken + "UtcExpiresOn" = [Datetime]::UtcNow.AddSeconds($TokenRequest.expires_in) + "RefreshToken" = $null + "Credential" = $credential + "ClientSecret" = $null + "ClientAssertion" = $null + "appid" = "" + "name" = $jwtToken.name + "upn" = $jwtToken.unique_name } if ($TokenRequest.PSObject.Properties.Name -eq 'refresh_token') { $authContext.RefreshToken = $TokenRequest.refresh_token @@ -203,14 +221,15 @@ try { throw "Invalid Access token" } $authContext += @{ - "AccessToken" = $accessToken - "UtcExpiresOn" = [Datetime]::UtcNow.AddSeconds($TokenRequest.expires_in) - "RefreshToken" = $TokenRequest.refresh_token - "Credential" = $null - "ClientSecret" = $null - "appid" = "" - "name" = $jwtToken.name - "upn" = $jwtToken.unique_name + "AccessToken" = $accessToken + "UtcExpiresOn" = [Datetime]::UtcNow.AddSeconds($TokenRequest.expires_in) + "RefreshToken" = $TokenRequest.refresh_token + "Credential" = $null + "ClientSecret" = $null + "ClientAssertion" = $null + "appid" = "" + "name" = $jwtToken.name + "upn" = $jwtToken.unique_name } if ($tenantID -eq "Common") { if (!$silent) { Write-Host "Authenticated to common, using tenant id $($jwtToken.tid)" } @@ -315,14 +334,15 @@ try { throw "Invalid Access token" } $authContext += @{ - "AccessToken" = $accessToken - "UtcExpiresOn" = [Datetime]::UtcNow.AddSeconds($TokenRequest.expires_in) - "RefreshToken" = $null - "Credential" = $null - "ClientSecret" = $null - "appid" = "" - "name" = $jwtToken.name - "upn" = $jwtToken.unique_name + "AccessToken" = $accessToken + "UtcExpiresOn" = [Datetime]::UtcNow.AddSeconds($TokenRequest.expires_in) + "RefreshToken" = $null + "Credential" = $null + "ClientSecret" = $null + "ClientAssertion" = $null + "appid" = "" + "name" = $jwtToken.name + "upn" = $jwtToken.unique_name } if ($TokenRequest.PSObject.Properties.Name -eq 'refresh_token') { $authContext.RefreshToken = $TokenRequest.refresh_token @@ -337,16 +357,17 @@ try { $accessToken = "N/A" $authContext.deviceCode = $deviceCode $authContext += @{ - "AccessToken" = $accessToken - "UtcExpiresOn" = [Datetime]::Now - "RefreshToken" = "" - "Credential" = $null - "ClientSecret" = $null - "appid" = "" - "name" = "" - "upn" = "" - "userCode" = $userCode - "message" = $message + "AccessToken" = $accessToken + "UtcExpiresOn" = [Datetime]::Now + "RefreshToken" = "" + "Credential" = $null + "ClientSecret" = $null + "ClientAssertion" = $null + "appid" = "" + "name" = "" + "upn" = "" + "userCode" = $userCode + "message" = $message } } } diff --git a/Auth/Renew-BcAuthContext.ps1 b/Auth/Renew-BcAuthContext.ps1 index 93c5ea478..a57f2447a 100644 --- a/Auth/Renew-BcAuthContext.ps1 +++ b/Auth/Renew-BcAuthContext.ps1 @@ -39,6 +39,7 @@ try { -refreshToken $bcAuthContext.RefreshToken ` -Scopes $bcAuthContext.Scopes ` -clientSecret $bcAuthContext.clientSecret ` + -clientAssertion $bcAuthContext.clientAssertion ` -credential $bcAuthContext.Credential ` -includeDeviceLogin:$bcAuthContext.includeDeviceLogin ` -deviceLoginTimeout $bcAuthContext.deviceLoginTimeout ` diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 288cc2807..218122014 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,6 +1,8 @@ 6.0.19 Issue #3560 Backup-BcContainerDatabases fails - password must be marked as read only bug Issue #3559 ALCops can't find "Business Foundation" app and doesn't start because of it +Support for federated credentials in Azure +Issue 1093 from AL-Go repo, generateDependencyArtifact doesn't work when useCompilerFolder is true 6.0.18 Use altool from prerelease AL Language extension