Public/Authentication.ps1
|
## Ensure moveSessions folder exists $moveSessionsFolderPath = Join-Path -Path $userPaths.credentials -ChildPath '.moveSessions' if (-not (Test-Path -Path $moveSessionsFolderPath -PathType Container)) { $null = New-Item -Path $moveSessionsFolderPath -ItemType Directory } ## Define a MoveSessions Variable to store connection details in New-Variable -Scope Global -Name 'MoveSessions' -Value @{ } -ErrorAction SilentlyContinue New-Variable -Scope Global -Name 'MoveSessionsPath' -Value (Join-Path -Path $userPaths.credentials -ChildPath '.moveSessions') -ErrorAction SilentlyContinue function New-MoveSession { <# .SYNOPSIS Creates a connection to a Move VM instance .DESCRIPTION This function creates a connection to the specified Move instance using the provided credentials. This session can be used when calling other functions within the Move module .PARAMETER SessionName The name that will be used when referring to the created MoveSession .PARAMETER Server The URI of the Move instance to connect to .PARAMETER Credential The credentials to be used twhen connecting to Move .PARAMETER AllowInsecureSSL Switch indicating whether or not an insecure SSL connection is allowed .EXAMPLE $Session = @{ SessionName = 'TMDDEV' Server = 'tmddev.Move.net' Credential = (Get-StoredCredential -Name 'ME') } New-MoveSession @Session .OUTPUTS None #> [CmdletBinding()] [Alias('Connect-MoveServer')] param( [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string]$Server, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [int]$Port = 443, [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [pscredential]$Credential, [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [switch]$AllowInsecureSSL, [Parameter( DontShow = $true, Mandatory = $false)] [switch]$Force, [Parameter( Mandatory = $false)] [switch]$Passthru ) begin { $SessionName = 'Default' # If there is no saved session, build the path to the one we will create if (-not $Force.IsPresent) { try { $savedSession = Get-MoveSession -Server $Server } catch { Write-Error "No Move Session found for [$Server]." } } } process { if ($savedSession) { Write-Verbose 'Returning existing session' $Global:MoveSessions[$savedSession.Name] = $savedSession return $Passthru.IsPresent ? $savedSession : $null } ## Create a session object for this new connection $NewMoveSession = [MoveSession]::new($SessionName, $Server, $AllowInsecureSSL) ## Trim the server name $Instance = $Server -replace '^https?://' ## Save the Instance and Port used $NewMoveSession.MoveServer = $Instance ## Prepare Request Headers for use in the Session Header Cache $ContentType = 'application/json;charset=UTF-8' # Authenticating with Move APIs - Basic AUTH over SSL $RequestHeaders = @{ 'Content-Type' = $ContentType 'Accept' = 'application/json' 'Cache-Control' = 'no-cache' } $body = @{ Spec = @{ Username = $Credential.UserName Password = $Credential.GetNetworkCredential().Password } } $WebRequestSplat = @{ Method = 'POST' Uri = 'https://{0}:{1}/move/v2/users/login' -f $Instance, $Port Headers = $RequestHeaders SessionVariable = 'MoveWebSession' PreserveAuthorizationOnRedirect = $true ContentType = $ContentType Body = $body | ConvertTo-Json -Compress ProgressAction = 'SilentlyContinue' SkipCertificateCheck = $AllowInsecureSSL.IsPresent } ## Attempt Login if ($VerbosePreference -eq 'Continue') { Write-Host 'Logging into Move instance [ ' -NoNewline Write-Host $Instance -ForegroundColor Cyan -NoNewline Write-Host ' ] as [ ' -NoNewline Write-Host $Credential.UserName -ForegroundColor Cyan -NoNewline Write-Host ' ]' } # Make the request try { Write-Verbose 'Invoking web request' $Response = Invoke-WebRequest @WebRequestSplat ## Check the Response code for 200 if ($Response.StatusCode -eq 200) { $ResponseContent = $Response.Content | ConvertFrom-Json } } catch { Write-Host $_.Exception.Message Write-Host $_.Exception.InnerException.Message throw $_ } ## Add this Session to the MoveSessions list and save it to disk Write-Verbose 'Populating NewMoveSession' $NewMoveSession.MoveSessionRefreshData = $Credential $MoveWebSession.Headers['Authorization'] = $ResponseContent.Status.Token $NewMoveSession.ExpirationDate = [datetime]::UnixEpoch.AddSeconds($ResponseContent.Status.ExpiryDate) $NewMoveSession.MoveWebSession = $MoveWebSession $sessionPath = Join-Path -Path $MoveSessionsPath -ChildPath 'Default.json' -ErrorAction Stop $NewMoveSession | ConvertTo-Json -Depth 10 | Out-File -FilePath $sessionPath -Force Write-Verbose 'Exported NewMoveSession, adding to MoveSessions' $Global:MoveSessions[$SessionName] = $NewMoveSession ## Return the session if requested if ($Passthru.IsPresent) { return $NewMoveSession } } } function Get-MoveSession { <# .SYNOPSIS Gets a MoveSession by Name, Server or Version .PARAMETER Name One or more MoveSession names to get .PARAMETER Server One or more TM servers for which a MoveSession has been created .PARAMETER Version One or more TM server versions for which a MoveSession has been created .EXAMPLE Get-MoveSession -Name 'Default', 'DEV' .EXAMPLE Get-MoveSession -Version '6.1.*' .EXAMPLE Get-MoveSession -Server '*.Move.net' .OUTPUTS One MoveSession, or an array of MoveSessions 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)] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [string]$Server, [Parameter( Mandatory = $false, ParameterSetName = 'ByVersion', ValueFromPipelineByPropertyName = $true)] [string[]]$Version, [Parameter( Mandatory = $false, Position = 0, ParameterSetName = 'ByObject')] [MoveSession]$MoveSession ) begin { # Is there a valid session in the Global:MoveSessions variable?} if (Test-MoveSession -MoveSession $Global:MoveSessions.Default) { Write-Verbose 'Returning valid session from Global var' $existingSession = $Global:MoveSessions.Default return # go to the process block } else { Write-Verbose 'No valid sessions in Global var' $credentialFromExpiredSession = $Global:MoveSessions.Default.MoveSessionRefreshData $Global:MoveSessions.Clear() } # Is there a valid session saved to disk? $message = 'Checking for a saved session:' switch ($PSCmdlet.ParameterSetName) { 'ByObject' { $message = "$message MoveSession Server = {0}" -f $MoveSession.MoveServer } 'ByServer' { $message = "$message Server = {0}" -f $Server } default { $message = "$message Name = {0}" -f $Name } } Write-Verbose $message $sessionPath = Join-Path -Path $MoveSessionsPath -ChildPath 'Default.json' try { $existingSessionJson = Get-Content -Path $sessionPath -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop $existingSession = [MoveSession]::new( 'Default', $existingSessionJson.MoveServer, $existingSessionJson.MovePort, $existingSessionJson.AllowInsecureSSL ) $existingWebSession = [Microsoft.PowerShell.Commands.WebRequestSession]::new() # $existingWebSession.Headers = $existingSessionJson.MoveWebSession.Headers foreach ($header in $existingSessionJson.MoveWebSession.Headers.psobject.Members | Where-Object membertype -EQ NoteProperty) { $existingWebSession.Headers.Add($header.Name, $header.Value) } $existingSession.MoveWebSession = $existingWebSession $cookies = [System.Net.CookieContainer]::new() $existingSessionJson.MoveWebSession.Cookies = $cookies if (Test-MoveSession -MoveSession $existingSession) { $Global:MoveSessions.Default = $existingSession Write-Verbose "Returning existing MoveSession from path: $sessionPath" } else { # there is no refresh token # create a new session try { Write-Verbose 'Creating New Session' # New-MoveSession -Server $existingSession.MoveServer -Port $existingSession.MovePort -Credential $existingSession.Credential $newSessionSplat = @{ Server = $Global:MoveSessions.Default.MoveServer ?? $existingSession.MoveServer Port = $Global:MoveSessions.Default.MovePort ?? $existingSession.MovePort Credential = $Global:MoveSessions.Default.MoveSessionRefreshData ?? $existingSession.MoveSessionRefreshData ?? $credentialFromExpiredSession AllowInsecureSSL = $Global:MoveSessions.Default.AllowInsecureSSL ?? $existingSession.AllowInsecureSSL } New-MoveSession @newSessionSplat -Force } catch { $Global:MoveSessions.Clear() Write-Verbose 'Error creating refreshed session' throw $_ } } } catch { Write-Verbose "No valid MoveSession found at path: $sessionPath" Write-Verbose "Reason: $_" $existingSession = $null } } process { if ($existingSession) { Write-Verbose "Returning existing MoveSession: $($existingSession.Name)@$($existingSession.MoveServer)" return $existingSession } [string] $StarMatch = '.*\*.*' $SessionsToReturn = switch ($PSCmdlet.ParameterSetName) { 'ByObject' { $Global:MoveSessions.Values | Where-Object { $_.Name -eq $MoveSession.Name -and $_.TMServer -eq $MoveSession.TMServer -and $_.TMVersion -eq $MoveSession.TMVersion } break } 'ByName' { if ( $Name -eq '*' ) { $Global:MoveSessions.Values } else { foreach ($SingleName in $Name) { $Global:MoveSessions.Values | Where-Object Name -Like $SingleName } } break } 'ByServer' { if ($Server.Count -eq 1 -and $Server -match $StarMatch) { # Adding a star at the end so we can match just by name and not by FQDN $Global:MoveSessions.Values | Where-Object MoveServer -Like "$Server*" } else { $Global:MoveSessions.Values | Where-Object MoveServer -In $Server } break } 'ByVersion' { if ($Version.Count -eq 1 -and $Version -match $StarMatch) { $Global:MoveSessions.Values | Where-Object MoveVersion -Like $Version } else { $Global:MoveSessions.Values | Where-Object MoveVersion -In $Version } break } } if ( $SessionsToReturn.Count ) { Write-Verbose "Returning $($SessionsToReturn.Count) sessions" return [MoveSession] $SessionsToReturn } switch ($PSCmdlet.ParameterSetName) { 'ByObject' { throw ('Unexpected error returning a MoveSession with Name: "{0}" on server "{1}"' -f ($MoveSession)?.Name, ($MoveSession).TMServer) } default { $MatchingProperty = $PSCmdlet.ParameterSetName -replace '^By' $MatchingPropertyValue = (Get-Variable $MatchingProperty -ErrorAction SilentlyContinue)?.Value if ( $MatchingPropertyValue -match $StarMatch) { Write-Verbose 'No MoveSessions found' return @{} } else { throw "MoveSession with provided $MatchingProperty '$MatchingPropertyValue' was not found" } } } } } function Test-MoveSession { <# .SYNOPSIS Tests the connection to a MoveSession .PARAMETER MoveSession The MoveSession to test .PARAMETER MoveSession Test with Server name only .EXAMPLE Test-MoveSession -MoveSession (Get-MoveSession -Name 'Default') .EXAMPLE Test-MoveSession -Server my.nutanix.move.net .OUTPUTS None #> [CmdletBinding()] param( [AllowNull()][Parameter( Mandatory = $false, Position = 0, ValueFromPipeline = $true, ParameterSetName = 'MoveSessionObject')] [pscustomobject]$MoveSession, [Parameter( Mandatory = $true, ParameterSetName = 'MoveSessionServer')] [string]$Server ) process { if ($PSCmdlet.ParameterSetName -eq 'MoveSessionServer') { $sessionPath = Join-Path -Path $MoveSessionsPath -ChildPath $Server if (Test-Path -Path $sessionPath -ErrorAction SilentlyContinue) { $NewMoveSession = Get-Content -Path $sessionPath -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop Test-MoveSession -MoveSession $NewMoveSession } else { return $false } } if ($null -eq $MoveSession) { return $false } $expirationDateTime = $MoveSession.ExpirationDate $now = Get-Date $isExpired = ($expirationDateTime - $now).totalseconds -lt 20 if ($isExpired) { Write-Verbose "it is expired: expirationDate = $expirationdatetime; now = $now" return $false } if ($MoveSession.MoveWebSession) { Write-Verbose "Testing Move Session [$($MoveSession.Name)] on server [$($MoveSession.MoveServer)]" $testTokenSplat = @{ Method = 'POST' Uri = 'https://{0}:{1}/move/v2/providers/list' -f $MoveSession.MoveServer, $MoveSession.MovePort Headers = @{ Authorization = "Bearer $($MoveSession.MoveWebSession.Headers.Authorization)" 'Content-Type' = 'application/json' 'Accept' = 'application/json' 'Cache-Control' = 'no-cache' } SkipCertificateCheck = $MoveSession.AllowInsecureSSL.IsPresent StatusCodeVariable = 'statusCode' } try { $null = Invoke-RestMethod @testTokenSplat if ($statusCode -eq 200) { Write-Verbose "Move Session [$($MoveSession.Name)] on server [$($MoveSession.MoveServer)] is valid" return $true } else { Write-Verbose "Move Session [$($MoveSession.Name)] on server [$($MoveSession.MoveServer)] is not valid, status code: $statusCode" return $false } } catch { Write-Verbose "Error: $_" return $false } } else { Write-Verbose "No Move Web Session found for server [$($MoveSession.TMServer)]" return $false } } } |