Public/Authentication.ps1
# Define a TMSessions Variable to store connection details in New-Variable -Scope global -Name 'TMSessions' -Value @{} -Force function New-TMSession { <# .SYNOPSIS Creates a connection to a TransitionManager instance .DESCRIPTION This function creates a connection to the specified TransitionManager instance using the provided credentials. This session can be used when calling other functions within the TransitionManager module .PARAMETER SessionName The name that will be used when referring to the created TMSession .PARAMETER Server The URI of the TransitionManager instance to connect to .PARAMETER Credential Specifies a PSCredential object to be used when connecting to TransitionManager .PARAMETER StoredCredential Specifies a string that will be used to search for a StoredCredential. If a matching StoredCredential is found, it will be used when connecting to TransitionManager .PARAMETER Project The name of the project on the TransitionManager instance to enter into .PARAMETER TMVersion Used to override the API version used by other functions in the TransitionManager module .PARAMETER AllowInsecureSSL Boolean indicating whether or not an insecure SSL connection is allowed .PARAMETER Passthru Switch indicating that the newly created TMSession should be output .PARAMETER ProfileName The name of the stored TMProfile which contains the connection settings .EXAMPLE $Session = @{ SessionName = 'TMDDEV' Server = 'tmddev.transitionmanager.net' Credential = (Get-StoredCredential -Name 'ME') Project = 'RD - VMware HCX' } New-TMSession @Session .EXAMPLE $Session = @{ SessionName = 'TMDDEV' Server = 'tmddev.transitionmanager.net' StoredCredential = 'JohnSmith' Project = 'RD - VMware HCX' } New-TMSession @Session .EXAMPLE Get-TMProfile -Name 'TMDDEV' | New-TMSession .EXAMPLE New-TMSession -ProfileName 'TMQA09' .OUTPUTS None #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] [CmdletBinding()] [Alias('Connect-TMServer')] param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] [String]$SessionName = 'Default', [Parameter(Mandatory = $true, ParameterSetName = 'ByStoredCredential', ValueFromPipelineByPropertyName = $true)] [Parameter(Mandatory = $true, ParameterSetName = 'ByPSCredential', ValueFromPipelineByPropertyName = $true)] [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [String]$Server, [Parameter(Mandatory = $true, ParameterSetName = 'ByPSCredential', ValueFromPipelineByPropertyName = $true)] [PSCredential]$Credential, [Parameter(Mandatory = $true, ParameterSetName = 'ByStoredCredential')] [String]$StoredCredential, [Parameter(Mandatory = $false, Position = 1, ValueFromPipelineByPropertyName = $true)] [Alias('Project')] [String]$ProjectName, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Object]$TMVersion, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Bool]$AllowInsecureSSL = $false, [Parameter(Mandatory = $false)] [Switch]$Passthru, [Parameter(Mandatory = $true, ParameterSetName = 'ByProfileObject')] [TMProfile]$TMProfile, [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByProfile')] [ArgumentCompleter( { Get-TMProfile -List })] [Alias('Profile')] [String]$ProfileName ) begin { try { if ($PSCmdlet.ParameterSetName -eq 'ByStoredCredential') { $Credential = Get-StoredCredential -Name $StoredCredential #Get-StoredCredential does not throw terminating errors if a matching StoredCredential isn't found #so need to throw one here to end the script if no matches are found. if (-not $Credential) { throw } } } catch { Write-Error "No StoredCredential named $StoredCredential was found." -ErrorAction Stop } ## Create the TMServers Array that will be reachable at $global:TMSessions if (-not $global:TMSessions) { New-Variable -Name TMSessions -Scope Global -Value @{} } } process { if ($ProfileName) { $TMProfile = Get-TMProfile -Name $ProfileName if (!$TMProfile) { Write-Error "Could not load a TransitionManager profile named '$ProfileName'" return } } if ($TMProfile) { $SessionName = (($global:TMSessions.Keys -contains 'Default') -and ($SessionName -eq 'Default')) ? $TMProfile.Name : 'Default' $Server = $TMProfile.Server $Credential = $TMProfile.Credential $AllowInsecureSSL = $TMProfile.AllowInsecureSSL if ([String]::IsNullOrWhiteSpace($Project)) { $Project = $TMProfile.Project } } else { if ($SessionName -eq 'Default') { $SessionName = (($global:TMSessions.Keys -contains 'Default') -and ($SessionName -eq 'Default')) ? $Server : 'Default' } } # Make sure Credential is the correct type if (-not ($Credential -is [PSCredential])) { throw "The value passed for the Credential parameter must be either a String with the name of a stored credential or a PSCredential." } # Trim the server name $Instance = [TMSession]::FormatServerUrl($Server) # Check for Existing Session to this server if ($global:TMSessions.Keys -contains $SessionName) { $NewTMSession = $global:TMSessions[$SessionName] } else { $NewTMSession = [TMSession]::new($SessionName, $Instance, $AllowInsecureSSL) } # Get the TM Version (Allow a different api usage of an alternate version) $Version = [TMSession]::ParseVersion($TMVersion) if ($Version -le '0.0.0') { $Version = Get-TMVersion -Server $Server -AllowInsecureSSL $AllowInsecureSSL } $NewTMSession.TMVersion = $Version if ($NewTMSession.TMVersion -lt '4.7' -or $NewTMSession.TMVersion -ge '7.0.0') { Write-Error "Unable to Log into $Instance. Version $($NewTMSession.TMVersion) not supported" return } # Prepare Request Headers for use in the Session Header Cache $ContentType = 'application/json;charset=UTF-8' $RequestHeaders = @{ "Accept-Version" = "1.0"; "Content-Type" = $ContentType; "Accept" = "application/json"; "Cache-Control" = "no-cache"; } $WebRequestSplat = @{ Method = 'POST' Uri = "https://$Instance/tdstm/auth/signIn" Headers = $RequestHeaders Body = ( @{ username = $Credential.UserName password = $Credential.GetNetworkCredential().Password } | ConvertTo-Json ) SessionVariable = 'TMWebSession' PreserveAuthorizationOnRedirect = $true ContentType = $ContentType SkipCertificateCheck = $AllowInsecureSSL } # Attempt Login if ($VerbosePreference -eq 'Continue') { Write-Host "Logging into TransitionManager instance [ " -NoNewline Write-Host $Instance -ForegroundColor Cyan -NoNewline Write-Host " ]" } # Make the request try { Write-Verbose "Web Request Parameters:" Write-Verbose ($WebRequestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $Response = Invoke-WebRequest @WebRequestSplat Write-Verbose "Response status code: $($Response.StatusCode)" Write-Verbose "Response Content: $($Response.Content)" } catch { throw $_ } # Check the Response code for 200 if ($Response.StatusCode -eq 200) { $ResponseContent = $Response | ConvertFrom-Json -Depth 10 # Ensure the login succeeded. if ($ResponseContent.PSObject.Properties.name -eq 'error' ) { # Report Error Condition Write-Error "Login Failed: $($ResponseContent[0].'error')" return } # Login Succeeded if ($ResponseContent.PSObject.Properties.name -notcontains 'userContext') { Write-Error "Login Failure! Unable to Log into $Server. Check the URL and Credentials and try again" return } } else { Write-Error "Unable to Log into $Server. Check the URL and Credentials and try again" return } $NewTMSession.TMWebSession = $TMWebSession $NewTMSession.ParseCallbackData($Response, [TMCallbackDataType]::WebService) # Login to the REST API if ($NewTMSession.TMVersion -ge '5.0.4') { $WebRequestSplat.Uri = "https://$Instance/tdstm/api/login" $WebRequestSplat.SessionVariable = 'TMRestSession' $WebRequestSplat.StatusCodeVariable = 'StatusCode' $WebRequestSplat.SkipHttpErrorCheck = $true # Make the request try { Write-Verbose "Web Request Parameters:" Write-Verbose ($WebRequestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $Response = Invoke-RestMethod @WebRequestSplat Write-Verbose "Response status code: $($Response.StatusCode)" Write-Verbose "Response Content: $($Response.Content)" } catch { throw $_ } if ($StatusCode -notin 200, 204) { throw "Could not login to the TransitionManager API. Status Code: $StatusCode" } $NewTMSession.TMRestSession = $TMRestSession $NewTMSession.ParseCallbackData($Response, [TMCallbackDataType]::REST) } else { Write-Verbose "TransitionManager version '$($NewTMSession.TMVersion)' does not support the REST API" } # Create the TMSessionObject that will be checked/used by the rest of the TransitionManager Modules $NewTMSession.Authentication.PublicKey = Get-TMPublicKey -Server $Instance -AllowInsecureSSL $AllowInsecureSSL # Apply all of the gathered tokens to the appropriate headers/cookies $NewTMSession.ApplyHeaderTokens() # Get the User Info $NewTMSession.GetUserAccount() # Add this Session to the TMSessions list $global:TMSessions[$SessionName] = $NewTMSession # Check that the correct projet is selected if ([String]::IsNullOrWhiteSpace($ProjectName)) { if ($VerbosePreference -eq 'Continue') { Write-Host "Login Successful! Entering Last Project used: [" -NoNewline Write-Host $NewTMSession.UserContext.Project.Name -ForegroundColor Cyan -NoNewline Write-Host "]" } } elseif ($NewTMSession.UserContext.Project.Name -ne $ProjectName) { Enter-TMProject -TMSession $SessionName -ProjectName $ProjectName } # Return the session if requested if ($Passthru) { $NewTMSession } } } function Get-TMVersion { <# .SYNOPSIS Gets the version of a given TransitionManager instance .DESCRIPTION This function queries the given TransitionManager server for it's version number and returns it in the specified format .PARAMETER Server The server to be checked .PARAMETER Format The format of the version string that is returned .PARAMETER AllowInsecureSSL Boolean indicating whether or not an insecure SSL connection is allowed .EXAMPLE Get-TMVersion -Server 'tmddev.transitionmanager.net' .OUTPUTS A string containing the server's version in the specified format #> [CmdletBinding()] [OutputType([String])] param( [Parameter(Mandatory = $true)] [String]$Server, [Parameter(Mandatory = $false)] [ValidateSet('VersionSemVer', 'SemVer', 'Minor')] [String]$Format = "SemVer", [Parameter(Mandatory = $false)] [Bool]$AllowInsecureSSL = $false ) try { $Instance = [TMSession]::FormatServerUrl($Server) $Uri = "https://$Instance/tdstm/auth/loginInfo" $Response = Invoke-WebRequest -Method Get -Uri $Uri -SkipCertificateCheck:$AllowInsecureSSL if ($Response.StatusCode -eq 200) { $BuildVersion = ($Response.Content | ConvertFrom-Json).data.buildVersion $Version = [TMSession]::ParseVersion($BuildVersion) if ($Version -eq [Version]::new()) { throw "Unable to determine TM Server Version from string: $BuildVersion" } $Version } else { throw "Response status code $($Response.StatusCode) does not indicate success" } } catch { Write-Warning "Could not determine TM Server Version: $($_.Exception.Message)" [Version]::new() } } function Get-TMUserAccount { <# .SYNOPSIS Gets details about the TM User's account .DESCRIPTION Queries TransitionManager for a User's account details .PARAMETER TMSession A [TMSession] object or the name of a TMSession .PARAMETER UserId The ID of the user for whom the account details will be retrieved .EXAMPLE # Gets the User account details for the user logged into the 'ProjectInstance' TMSession Get-TMUserAccount -TMSession 'ProjectInstance' .EXAMPLE # Gets the User account details for the user with ID 632 Get-TMUserAccount -UserId 632 .OUTPUTS A [TMUserAccount] object representing the requested user's details #> [CmdletBinding()] param( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('Session', 'Name')] [Object]$TMSession = 'Default', [Parameter(Mandatory = $false)] [Alias('Id')] [Int32]$UserId ) process { # Verify/Retrieve the TMSession if ($TMSession -is [String]) { $TMSession = Get-TMSession $TMSession if (-not $TMSession) { throw "TMSession '$TMSession' not found. Check name or provide a [TMSession] object." } } if (-not ($TMSession -is [TMSession])) { throw "The value for the TMSession parameter must be of type [String] or [TMSession]" } # Use the TMSession's user if an Id wasn't provided if (-not $UserId) { $UserId = $TMSession.UserContext.User.Id } # Form the request Write-Verbose "Forming web request" $RestSplat = @{ Method = 'GET' Uri = "https://$($TMSession.TMServer)/tdstm/ws/user/$UserId" WebSession = $TMSession.TMWebSession StatusCodeVariable = 'StatusCode' SkipHttpErrorCheck = $true SkipCertificateCheck = $TMSession.AllowInsecureSSL } try { Write-Debug "Web Request Parameters:" Write-Debug ($RestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $Response = Invoke-RestMethod @RestSplat Write-Debug "Response status code: $StatusCode" Write-Debug "Response Content: $($Response | ConvertTo-Json -Depth 10 -ErrorAction SilentlyContinue)" } catch { throw $_ } if ($StatusCode -in 200, 204) { [TMUserAccount]::new($Response.data) } else { throw "The Status Code $StatusCode does not indicate success" } } } function Get-TMPublicKey { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String]$Server, [Parameter(Mandatory = $false)] [Boolean]$AllowInsecureSSL = $false ) $Server = [TMSession]::FormatServerUrl($Server) # Check for a version 4.7, 5.0, 5.1 $Uri = "https://$Server/tdstm/auth/loginInfo?secure=true" try { $Response = Invoke-WebRequest -Method Get -Uri $Uri -SkipCertificateCheck:$AllowInsecureSSL if ($Response.StatusCode -in 200, 204) { ($Response.Content | ConvertFrom-Json).data.publicKey } else { throw "The response status code does not indicate success" } } catch { Write-Warning "Could not get public key information: $($_.Exception.Message)" } } function Get-TMSession { <# .SYNOPSIS Gets a TMSession by Name, Server or Version .PARAMETER Name One or more TMSession names to get .PARAMETER Server One or more TM servers for which a TMSession has been created .PARAMETER Version One or more TM server versions for which a TMSession has been created .EXAMPLE Get-TMSession -Name 'Default', 'TMDDEV' .EXAMPLE Get-TMSession -Version '5.0.*' .EXAMPLE Get-TMSession -Server '*.transitionmanager.net' .OUTPUTS One TMSession, or an array of TMSessions depending on the number of sessions to return #> [CmdletBinding(DefaultParameterSetName = 'ByName')] param( [Parameter(Mandatory = $false, Position = 0, ParameterSetName = 'ByName', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('SessionName')] [String[]]$Name = "*", [Parameter(Mandatory = $false, ParameterSetName = 'ByServer', ValueFromPipelineByPropertyName = $true)] [Alias('TMServer')] [String[]]$Server, [Parameter(Mandatory = $false, ParameterSetName = 'ByVersion', ValueFromPipelineByPropertyName = $true)] [Alias('TMVersion')] [String[]]$Version ) process { switch ($PSCmdlet.ParameterSetName) { 'ByName' { if ( $Name -eq "*" ) { return $Global:TMSessions.Values } return $Global:TMSessions.Values | Where-Object Name -in $Name } 'ByServer' { if ($Server.Count -eq 1 -and $Server[0][-1] -eq '*') { return $Global:TMSessions.Values | Where-Object TMServer -like "$Server*" } return $Global:TMSessions.Values | Where-Object TMServer -in $Server } 'ByVersion' { if ($Version.Count -eq 1 -and $Version[0][-1] -eq '*') { return $Global:TMSessions.Values | Where-Object TMVersion -like "$Version*" } return $Global:TMSessions.Values | Where-Object TMVersion -in $Version } } } } function Remove-TMSession { <# .SYNOPSIS Disconnects one or more sessions with a TransitionManager instance .DESCRIPTION This function signs out of one or more TransitionManager instances and removes the session from the $Global:TMSessions variable .PARAMETER Name The name of one or more TMSessions to disconnect and remove .PARAMETER InputObject One or more TMSessions to disconnect and remove .EXAMPLE Remove-TMSession -Name 'Default', 'TMDDEV' .EXAMPLE Get-TMSession | Remove-TMSession .OUTPUTS None #> [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByName')] [Alias('SessionName')] [String[]]$Name, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject')] [Object[]]$InputObject ) process { Write-Verbose "Parameter Set: $($PSCmdlet.ParameterSetName)" # Compile a list of the sessions that need to be disconnected $SessionsToRemove = @() switch ($PSCmdlet.ParameterSetName) { 'ByName' { $Name | ForEach-Object { $SessionsToRemove += $Global:TMSessions[$_] } } 'ByObject' { $SessionsToRemove = $InputObject } } # Iterate over each session that was passed and sign out of TM foreach ($Session in $SessionsToRemove) { Write-Host "Logging out of TransitionManager instance [ " -NoNewline Write-Host $Session.TMServer -ForegroundColor Cyan -NoNewline Write-Host " ]" # Format the parameters for the sign out request $WebRequestSplat = @{ Method = 'POST' Uri = "https://$($Session.TMServer)/tdstm/auth/signOut" WebSession = $Session.TMWebSession ContentType = 'application/json;charset=UTF-8' PreserveAuthorizationOnRedirect = $true SkipCertificateCheck = $Session.AllowInsecureSSL } try { # Make the request to sign out $Response = Invoke-WebRequest @WebRequestSplat if ($Response.StatusCode -eq 200) { # Ensure the sign out was successful $ResponseContent = $Response.Content | ConvertFrom-Json if ($ResponseContent.status -ne 'success') { Write-Error ($ResponseContent.errors -join '; ') } Write-Host "Log out successful!" # Remove the session from the global sessions variable $Global:TMSessions.Remove($Session.Name) } else { Write-Error "Could not sign out of TransitionManager" } } catch { Write-Error $_ } } } } function New-TMProfile { <# .SYNOPSIS Creates a TransitionManager connection profile on the local hard drive .DESCRIPTION This function creates a TransitionManager connection profile on the localk hard drive that can be used with the New-TMSession function to more easily create connections to TransitionManager instances .PARAMETER Name The name name of the profile that will be saved. This name will be used to reference the TMProfile in other functions. .PARAMETER Server The URI of the TransitionManager instance .PARAMETER Project The name of the project on the TransitionManager instance .PARAMETER Credential The credentials used to connect to TransitionManager .PARAMETER AllowInsecureSSL Boolean indicating whether or not an insecure SSL connection is allowed .PARAMETER Passthru Switch indicating that the newly created TMProfile should be output .EXAMPLE New-TMProfile -Name 'TMDDEV-RVTools' -Server 'tmddev.transitionmanager.net' -Project 'RD - RVTools' -Credential (Get-Credential) .EXAMPLE $Profile = @{ Name = 'TMDDEV2' Server = 'tmddev2.transitionmanager.net' Credential = (Get-StoredCredential -Name 'ME') } New-TMProfile @Profile -Passthru | New-TMSession .OUTPUTS If Passthru switch is used, a TMProfile object. Otherwise, none #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String]$Name, [Parameter(Mandatory = $true)] [String]$Server, [Parameter(Mandatory = $false)] [String]$Project, [Parameter(Mandatory = $true)] [PSCredential]$Credential, [Parameter(Mandatory = $false)] [Bool]$AllowInsecureSSL = $false, [Parameter(Mandatory = $false)] [Switch]$Passthru = $false ) process { $ProfileDirectory = Join-Path -Path $HOME -ChildPath 'TMD_Files' -AdditionalChildPath 'Profiles' Test-FolderPath -FolderPath $ProfileDirectory $TMProfile = [TMProfile]::new($Name, $Server, $Project, $Credential, $AllowInsecureSSL) $TMProfile | Export-Clixml -Path (Join-Path -Path $ProfileDirectory -ChildPath "$Name.tmprofile") if ($Passthru) { $TMProfile } } } function Get-TMProfile { <# .SYNOPSIS Gets a TMProfile that is stored on the local hard drive .DESCRIPTION Gets one or more of the TransitionManager profiles that are saved on the local hard drive .PARAMETER Name The name of the TMProfile to get .PARAMETER List Switch indicating that a list of all TMProfile names should be returned .EXAMPLE $AllProfiles = Get-TMProfile .EXAMPLE Get-TMProfile -Name TMDDEV2 .EXAMPLE Get-TMProfile -List .OUTPUTS One or more objects representing a saved TMProfile #> [CmdletBinding(DefaultParameterSetName = "Single")] [OutputType([TMProfile])] param( [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "Single")] [ArgumentCompleter( { Get-TMProfile -List })] [String]$Name, [Parameter(Mandatory = $true, ParameterSetName = "List")] [Switch]$List ) process { try { if (-not $Name -or $List) { $ProfileDirectory = Join-Path -Path $HOME -ChildPath 'TMD_Files' -AdditionalChildPath 'Profiles' Test-FolderPath -Path $ProfileDirectory $ProfileFiles = Get-ChildItem -Path $ProfileDirectory -Filter '*.tmprofile' -File $ProfileFiles | ForEach-Object { if ($List) { $_.Name -replace $_.Extension, '' } else { $TMProfile = Import-Clixml -Path $_.FullName [TMProfile]::new( $TMProfile.Name, $TMProfile.Server, $TMProfile.Project, $TMProfile.Credential, $TMProfile.AllowInsecureSSL ) } } } else { $ProfileFilePath = Join-Path -Path $HOME -ChildPath 'TMD_Files' -AdditionalChildPath 'Profiles', ($Name + '.tmprofile') if (Test-Path -Path $ProfileFilePath) { $TMProfile = Import-Clixml -Path $ProfileFilePath [TMProfile]::new( $TMProfile.Name, $TMProfile.Server, $TMProfile.Project, $TMProfile.Credential, $TMProfile.AllowInsecureSSL ) } } } catch { $PSCmdlet.WriteError($_) } } } function Remove-TMProfile { <# .SYNOPSIS Removes a previously stored TMProfile from the local hard drive .DESCRIPTION Overwrites a stored .tmprofile file multiple times with a random bytestream before deleting it from the local hard drive .PARAMETER Name The name of the TMProfile to be removed .PARAMETER OverwriteCount The number of times to overwrite the file before removing it .EXAMPLE Remove-TMProfile -Name 'TMDDEV2' .EXAMPLE Get-TMProfile | Remove-TMProfile .NOTES General notes #> [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ArgumentCompleter( { Get-TMProfile -List })] [String]$Name, [Parameter(Mandatory = $false)] [Int]$OverwriteCount = 500 ) process { $ProfileFilePath = Join-Path -Path $HOME -ChildPath 'TMD_Files' -AdditionalChildPath 'Profiles', ($Name + '.tmprofile') Write-Verbose "Processing file '$ProfileFilePath'" if (Test-Path -PathType Leaf -Path $ProfileFilePath) { $BufferSize = $Name.Length $RandomDataBuffer = [System.Byte[]]::new($BufferSize) Write-Verbose "Overwriting the data within the file $OverwriteCount time(s)" for ($i = 0; $i -lt $OverwriteCount; $i++) { $Random = [System.Random]::new() $Random.NextBytes($RandomDataBuffer) | Set-Content -Path $ProfileFilePath -AsByteStream } Write-Verbose "Removing the file" Remove-Item -Path $ProfileFilePath -Force } else { $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( [Exception]::new("A TransitionManager profile with the name '$Name' does not exist."), "TMD.AUTH02", [System.Management.Automation.ErrorCategory]::InvalidArgument, $ProfileFilePath ) $PSCmdlet.WriteError($ErrorRecord) } } } function Update-TMSessionToken { <# .SYNOPSIS Updates the REST API access token .DESCRIPTION Refreshes the REST API access token in the TMRestSession of a TMSession object and updates the appropriate headers .PARAMETER TMSession A [TMSession] object or the name of a TMSession .EXAMPLE # Get an existing TMSession and pipe it to Update-TMSessionToken Get-TMSession -Name 'TMAD61' | Update-TMSessionToken .EXAMPLE $Session = New-TMSession -Server 'tmad61.transitionmanager.net' -Credential (Get-StoredCredential -Name ME) -Passthru Update-TMSessionToken -TMSession $Session .EXAMPLE # Updates the access token of the 'Default' TMSession Update-TMSessionToken .EXAMPLE # Updates the access token of the TMSession named 'ExampleSession' Update-TMSessionToken -TMSession 'ExampleSession' .OUTPUTS None #> [CmdletBinding()] param( [Parameter(Mandatory = $false, ValueFromPipeline = $true)] [Alias('Session', 'Name')] [Object]$TMSession = 'Default' ) process { if ($TMSession -is [String]) { $TMSession = Get-TMSession $TMSession if (-not $TMSession) { throw "TMSession '$TMSession' not found. Check name or provide a [TMSession] object." } } if (-not ($TMSession -is [TMSession])) { throw "The value for the TMSession parameter must be of type [String] or [TMSession]" } # Make sure refresh token is present in the TMSession if ([String]::IsNullOrWhiteSpace($TMSession.Authentication.OAuth.RefreshToken)) { throw "A refresh token could not be found in the TMSession" } # Make sure a JSessionID is present in the TMSession if ([String]::IsNullOrWhiteSpace($TMSession.Authentication.JSessionId)) { # Extract the JSessionID from one of the cookies if needed $ExistingJsessionCookies = @() $ExistingJsessionCookies += ($this.TMWebSession.Cookies.GetAllCookies() | Where-Object Name -eq 'JSESSIONID') $ExistingJsessionCookies += ($this.TMRestSession.Cookies.GetAllCookies() | Where-Object Name -eq 'JSESSIONID') if ($ExistingJsessionCookies.Count -gt 0) { $TMSession.Authentication.JSessionId = $ExistingJsessionCookies[0].Value $TMSession.ApplyHeaderTokens() } else { throw "No JSESSIONID cookie could be found in the TMSession." } } # Make sure the CSRF token is present in the TMSession if ([String]::IsNullOrWhiteSpace($TMSession.Authentication.CsrfToken)) { if ($TMSession.TMWebSession.Headers.Keys -contains $TMSession.Authentication.CsrfHeaderName) { $TMSession.Authentication.CsrfToken = $TMSession.TMWebSession.Headers[$TMSession.Authentication.CsrfHeaderName] $TMSession.ApplyHeaderTokens() } else { throw "A CSRF token could not be found in the TMSession" } } # (Re)apply the tokens to the web sessions' headers # Cache the token info $OldToken = $TMSession.Authentication.OAuth.AccessToken # Form the request headers Write-Verbose "Forming web request" $TMSession.TMRestSession.Headers.'Content-Type' = "application/json" $TMSession.TMRestSession.Headers.'Accept' = "application/json" $RestSplat = @{ Method = 'POST' Uri = "https://$($TMSession.TMServer)/tdstm/oauth/access_token" WebSession = $TMSession.TMRestSession StatusCodeVariable = 'StatusCode' SkipHttpErrorCheck = $true SkipCertificateCheck = $true Form = @{ 'grant_type' = 'refresh_token' 'refresh_token' = $TMSession.UserAccount.IsLocalAccount ? $TMSession.Authentication.OAuth.RefreshToken : $TMSession.Authentication.OAuth.AccessToken } } try { Write-Debug "Web Request Parameters:" Write-Debug ($RestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $Response = Invoke-RestMethod @RestSplat Write-Debug "Response status code: $StatusCode" Write-Debug "Response Content: $($Response | ConvertTo-Json)" } catch { throw $_ } switch ($StatusCode) { { $_ -in 200, 204 } { if ($Response.status -eq 'error') { throw "Error updating the access token: $($Response.errors)" } else { Write-Verbose "Applying new tokens" $TMSession.ParseCallbackData($Response, [TMCallbackDataType]::REST) $TMSession.ApplyHeaderTokens() # Make sure the token actually got updated if ($TMSession.Authentication.OAuth.AccessToken -eq $OldToken) { throw "A new token was not granted by the server" } } } 401 { throw "The request was unauthorized. Use New-TMsession to log into TransitionManager again" } 403 { throw "The server refused the request. It is likely that the original token has expired. Use New-TMSession to receive a new access token." } default { throw "The response status code $StatusCode did not indicate success: $Response" } } } } |