From e215540e6b34bc9413f5fde14768d682eb5dcb7a Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:13:09 -0400 Subject: [PATCH 01/12] reverting main versions --- Installer/build.ps1 | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Installer/build.ps1 b/Installer/build.ps1 index 73d04a359..670e82e62 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -18,7 +18,7 @@ $ADV_INST_HOME = "C:\Program Files (x86)\Caphyon\Advanced Installer ${ADV_INST_V $SIGNTOOL="${ADV_INST_HOME}\third-party\winsdk\x64\signtool.exe" $ADVINST = "${ADV_INST_HOME}\bin\x86\AdvancedInstaller.com" $ADVPROJECT = "${scriptPath}\ZitiDesktopEdge.aip" -$ZITI_EDGE_TUNNEL_VERSION="v2.0.0-alpha10" +$ZITI_EDGE_TUNNEL_VERSION="v1.1.0" echo "Cleaning previous build folder if it exists" Remove-Item "${buildPath}" -r -ErrorAction Ignore diff --git a/version b/version index 4dae5f4ab..0131a90c3 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.5.0.2 +2.4.0.2 \ No newline at end of file From 4cb30d125f5fb0400e5bfc69132776164efaff5d Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 25 Jul 2024 22:13:56 -0400 Subject: [PATCH 02/12] update build-test-release --- build-test-release.ps1 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/build-test-release.ps1 b/build-test-release.ps1 index 69e32f3fa..a8a2ace31 100644 --- a/build-test-release.ps1 +++ b/build-test-release.ps1 @@ -9,10 +9,10 @@ param( [Parameter(Mandatory = $true)] [string]$version, [string]$url = "http://localhost:8000/local", - [string]$stream = "beta", + [string]$stream = "local", [datetime]$published_at = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"), [bool]$jsonOnly = $false, - [bool]$revertGitAfter = $false + [switch]$keepGitAfter ) echo "" $env:ZITI_DESKTOP_EDGE_DOWNLOAD_URL="$url" @@ -36,9 +36,12 @@ if(! $jsonOnly) { $outputPath = "${version}.json" & .\Installer\output-build-json.ps1 -version $version -url $url -stream $stream -published_at $published_at -outputPath $outputPath Copy-Item -Force "$outputPath" "$scriptDirectory\release-streams\local\${version}" +Copy-Item -Force "$outputPath" "$scriptDirectory\release-streams\local\${version}\test.json" Copy-Item -Force "${version}.json" "$scriptDirectory\release-streams\${stream}.json" -echo "json file written to: $scriptDirectory\release-streams\${stream}.json" -if($revertGitAfter) { +if(!$keepGitAfter) { git checkout DesktopEdge/Properties/AssemblyInfo.cs ZitiUpdateService/Properties/AssemblyInfo.cs Installer/ZitiDesktopEdge.aip -} \ No newline at end of file +} +echo "Done" +echo "json file written to: $scriptDirectory\release-streams\${stream}.json" +echo "run a python server to test: python -m http.server 8000 -d .\release-streams" \ No newline at end of file From e9f513b614450ea1e9e762465e2457ee8dab5335 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:45:20 -0400 Subject: [PATCH 03/12] updates for 2.4.1.0 --- Installer/build.ps1 | 55 +++++++++++++++++++++++---------- Installer/output-build-json.ps1 | 9 +++--- build-test-release.ps1 | 50 +++++++++++++++++++----------- release-streams/beta.json | 10 +++--- version | 2 +- 5 files changed, 82 insertions(+), 44 deletions(-) diff --git a/Installer/build.ps1 b/Installer/build.ps1 index 670e82e62..351da359e 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -1,3 +1,13 @@ +param( + [string]$version, + [string]$url = "https://github.com/openziti/desktop-edge-win/releases/download/", + [string]$stream = "beta", + [datetime]$published_at = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"), + [bool]$jsonOnly = $false, + [bool]$revertGitAfter = $true, + [string]$versionQualifier = "" +) + $ErrorActionPreference = "Stop" function verifyFile($path) { @@ -18,7 +28,7 @@ $ADV_INST_HOME = "C:\Program Files (x86)\Caphyon\Advanced Installer ${ADV_INST_V $SIGNTOOL="${ADV_INST_HOME}\third-party\winsdk\x64\signtool.exe" $ADVINST = "${ADV_INST_HOME}\bin\x86\AdvancedInstaller.com" $ADVPROJECT = "${scriptPath}\ZitiDesktopEdge.aip" -$ZITI_EDGE_TUNNEL_VERSION="v1.1.0" +$ZITI_EDGE_TUNNEL_VERSION="v1.1.4" echo "Cleaning previous build folder if it exists" Remove-Item "${buildPath}" -r -ErrorAction Ignore @@ -35,7 +45,7 @@ if($null -eq $env:ZITI_EDGE_TUNNEL_BUILD) { } echo "========================== fetching ziti-edge-tunnel ==========================" $zet_dl="https://github.com/openziti/ziti-tunnel-sdk-c/releases/download/${ZITI_EDGE_TUNNEL_VERSION}/ziti-edge-tunnel-Windows_x86_64.zip" - echo "Beginning to download ziti-edge-tunnel from ${zet_dl}" + echo "Beginning to download ziti-edge-tunnel from ${zet_dl} to ${destination}" echo "" $response = Invoke-WebRequest $zet_dl -OutFile "${destination}" } else { @@ -77,16 +87,21 @@ msbuild ZitiDesktopEdge.sln /property:Configuration=Release Pop-Location -$installerVersion=(Get-Content -Path ${checkoutRoot}\version) +if ($version -eq "") { + $version=(Get-Content -Path ${checkoutRoot}\version) +} + +echo "Building VERSION $version" + if($null -ne $env:ZITI_DESKTOP_EDGE_VERSION) { - echo "ZITI_DESKTOP_EDGE_VERSION is set. Using that: ${env:ZITI_DESKTOP_EDGE_VERSION} instead of version found in file ${installerVersion}" - $installerVersion=$env:ZITI_DESKTOP_EDGE_VERSION - echo "Version set to: ${installerVersion}" + echo "ZITI_DESKTOP_EDGE_VERSION is set. Using that: ${env:ZITI_DESKTOP_EDGE_VERSION} instead of version found in file ${version}" + $version=$env:ZITI_DESKTOP_EDGE_VERSION + echo "Version set to: ${version}" } $action = '/SetVersion' -echo "issuing $ADVINST /edit $ADVPROJECT $action $installerVersion (service version: $serviceVersion) - see https://www.advancedinstaller.com/user-guide/set-version.html" -& $ADVINST /edit $ADVPROJECT $action $installerVersion +echo "issuing $ADVINST /edit $ADVPROJECT $action $version (service version: $serviceVersion) - see https://www.advancedinstaller.com/user-guide/set-version.html" +& $ADVINST /edit $ADVPROJECT $action $version $action = '/build' echo "Assembling installer using AdvancedInstaller at: $ADVINST $action $ADVPROJECT" @@ -103,7 +118,7 @@ if($gituser -eq "ziti-ci") { } $outputPath="${scriptPath}\Output" -$exeName="Ziti Desktop Edge Client-${installerVersion}.exe" +$exeName="Ziti Desktop Edge Client-${version}.exe" $exeAbsPath="${outputPath}\${exeName}" if($null -eq $env:AWS_KEY_ID) { @@ -118,15 +133,23 @@ if($null -eq $env:OPENZITI_P12_PASS_2024) { echo "" } else { echo "adding additional signature to executable with openziti.org signing certificate" - echo "Using ${SIGNTOOL} to sign executable with the additional OpenZiti signature" + echo "Using ${SIGNTOOL} to sign executable with the additional OpenZiti signature: ${exeAbsPath}" & "$SIGNTOOL" sign /f "${scriptPath}\openziti_2024.p12" /p "${env:OPENZITI_P12_PASS_2024}" /tr http://ts.ssl.com /fd sha512 /td sha512 /as "${exeAbsPath}" } -(Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256" +(Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${version}.exe.sha256" echo "========================== build.ps1 completed ==========================" -$defaultRootUrl = "https://github.com/openziti/desktop-edge-win/releases/download/" -$defaultStream = "beta" -$defaultPublishedAt = Get-Date -$updateJson = "${installerVersion}.json" -& .\Installer\output-build-json.ps1 -version $installerVersion -url $defaultRootUrl -stream $defaultStream -published_at $defaultPublishedAt -outputPath $updateJson +$outputPath = "${scriptPath}\Output\Ziti Desktop Edge Client-${version}.exe.json" +& .\Installer\output-build-json.ps1 -version $version -url $url -stream $stream -published_at $published_at -outputPath $outputPath -versionQualifier $versionQualifier + +echo "REMOVING .back files: ${scriptPath}\*back*" +Remove-Item "${scriptPath}\*back*" -Recurse -ErrorAction SilentlyContinue + +echo "Copying json file to beta.json" +copy $outputPath "$checkoutRoot\release-streams\beta.json" + + +if($revertGitAfter) { + git checkout DesktopEdge/Properties/AssemblyInfo.cs ZitiUpdateService/Properties/AssemblyInfo.cs Installer/ZitiDesktopEdge.aip +} diff --git a/Installer/output-build-json.ps1 b/Installer/output-build-json.ps1 index bca078a90..deb384274 100644 --- a/Installer/output-build-json.ps1 +++ b/Installer/output-build-json.ps1 @@ -5,10 +5,11 @@ param( [Parameter(Mandatory = $true)] [string]$version, - [string]$url = "http://localhost:8000/local", - [string]$stream = "beta", + [string]$url = "http://localhost:8000/release-streams/local", + [string]$stream = "local", [string]$outputPath = "${version}.json", - [datetime]$published_at + [datetime]$published_at, + [string]$versionQualifier = "" ) echo "=========== emitting a json file that represents this build ============" @@ -29,7 +30,7 @@ $jsonTemplate = @" "assets": [ { "name": "Ziti.Desktop.Edge.Client-${version}.exe", - "browser_download_url": "${url}/${version}/Ziti.Desktop.Edge.Client-${version}.exe" + "browser_download_url": "${url}/${version}${versionQualifier}/Ziti.Desktop.Edge.Client-${version}.exe" } ] } diff --git a/build-test-release.ps1 b/build-test-release.ps1 index a8a2ace31..a33b04745 100644 --- a/build-test-release.ps1 +++ b/build-test-release.ps1 @@ -8,40 +8,54 @@ param( [Parameter(Mandatory = $true)] [string]$version, - [string]$url = "http://localhost:8000/local", + [string]$url = "http://localhost:8000/release-streams/local", [string]$stream = "local", [datetime]$published_at = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"), [bool]$jsonOnly = $false, - [switch]$keepGitAfter + [bool]$revertGitAfter = $true, + [string]$versionQualifier = "" ) echo "" $env:ZITI_DESKTOP_EDGE_DOWNLOAD_URL="$url" $env:ZITI_DESKTOP_EDGE_VERSION="$version" $scriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Path -Parent +$outputPath = "$scriptDirectory\release-streams\${version}.json" +& .\Installer\output-build-json.ps1 -version $version -url $url -stream $stream -published_at $published_at -outputPath $outputPath + +Copy-Item -Force "$scriptDirectory\release-streams\${version}.json" "$scriptDirectory\release-streams\${stream}.json" +echo "json file written to: $scriptDirectory\release-streams\${stream}.json" + if(! $jsonOnly) { - $scriptToExecute = Join-Path -Path $scriptDirectory -ChildPath "Installer\build.ps1" - & $scriptToExecute + & .\Installer\build.ps1 -version $version -url $url -stream $stream -published_at $published_at -jsonOnly $jsonOnly -revertGitAfter $revertGitAfter -versionQualifier $versionQualifier $exitCode = $LASTEXITCODE if($exitCode -gt 0) { Write-Host "build.ps1 failed!" exit $exitCode } - Write-Host "only updating the json at $scriptDirectory\release-streams\${stream}.json" - mkdir $scriptDirectory\release-streams\local\${version} -ErrorAction Ignore > $null - Move-Item -Force "./Installer/Output/Ziti Desktop Edge Client-${version}.exe" "$scriptDirectory\release-streams\local\${version}\Ziti.Desktop.Edge.Client-${version}.exe" - Move-Item -Force "./Installer/Output/Ziti Desktop Edge Client-${version}.exe.sha256" "$scriptDirectory\release-streams\local\${version}\Ziti.Desktop.Edge.Client-${version}.exe.sha256" + + mkdir "$scriptDirectory\release-streams\local\${version}" -ErrorAction Ignore > $null + #echo "$scriptDirectory/Installer/Output/Ziti Desktop Edge Client-${version}.exe" "$scriptDirectory\release-streams\local\${version}\Ziti.Desktop.Edge.Client-${version}.exe" + Move-Item -Force "$scriptDirectory/Installer/Output/Ziti Desktop Edge Client-${version}.exe" "$scriptDirectory\release-streams\local\${version}\Ziti.Desktop.Edge.Client-${version}.exe" + Move-Item -Force "$scriptDirectory/Installer/Output/Ziti Desktop Edge Client-${version}.exe.sha256" "$scriptDirectory\release-streams\local\${version}\Ziti.Desktop.Edge.Client-${version}.exe.sha256" + Write-Host "" + Write-Host "done." + Write-Host "installer exists at $scriptDirectory\release-streams\local\${version}\Ziti.Desktop.Edge.Client-${version}.exe" } -$outputPath = "${version}.json" -& .\Installer\output-build-json.ps1 -version $version -url $url -stream $stream -published_at $published_at -outputPath $outputPath -Copy-Item -Force "$outputPath" "$scriptDirectory\release-streams\local\${version}" -Copy-Item -Force "$outputPath" "$scriptDirectory\release-streams\local\${version}\test.json" -Copy-Item -Force "${version}.json" "$scriptDirectory\release-streams\${stream}.json" - -if(!$keepGitAfter) { +if($revertGitAfter) { git checkout DesktopEdge/Properties/AssemblyInfo.cs ZitiUpdateService/Properties/AssemblyInfo.cs Installer/ZitiDesktopEdge.aip } -echo "Done" -echo "json file written to: $scriptDirectory\release-streams\${stream}.json" -echo "run a python server to test: python -m http.server 8000 -d .\release-streams" \ No newline at end of file + +Write-Host "" +Write-Host "Dependencies from ziti-edge-tunnel:" +Write-Host "---------------------------------------------" +& '.\Installer\build\service\ziti-edge-tunnel.exe' version -v +Write-Host "" + +Write-Host "Start a python server in this location with:" +Write-Host "" +Write-Host " python -m http.server 8000" +Write-Host "" +Write-Host "Set the automatic upgrade url to http://localhost:8000/release-streams/local.json" +Write-Host "" diff --git a/release-streams/beta.json b/release-streams/beta.json index 4f4ebc326..69a64da10 100644 --- a/release-streams/beta.json +++ b/release-streams/beta.json @@ -1,12 +1,12 @@ { - "name": "2.4.0.0", - "tag_name": "2.4.0.0", - "published_at": "2024-07-01T18:18:53Z", + "name": "2.4.1.0", + "tag_name": "2.4.1.0", + "published_at": "2024-09-23T05:38:06Z", "installation_critical": false, "assets": [ { - "name": "Ziti.Desktop.Edge.Client-2.4.0.0.exe", - "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.4.0.0/Ziti.Desktop.Edge.Client-2.4.0.0.exe" + "name": "Ziti.Desktop.Edge.Client-2.4.1.0.exe", + "browser_download_url": "http://localhost:8000/release-streams/local/2.4.1.0/Ziti.Desktop.Edge.Client-2.4.1.0.exe" } ] } diff --git a/version b/version index 0131a90c3..42dece39c 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.4.0.2 \ No newline at end of file +2.4.1.0 From fb3e707e4bd6f30fca2a3e5c1925368bc832483a Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:50:56 -0400 Subject: [PATCH 04/12] fix url --- release-streams/beta.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-streams/beta.json b/release-streams/beta.json index 69a64da10..da74e2e3c 100644 --- a/release-streams/beta.json +++ b/release-streams/beta.json @@ -1,12 +1,12 @@ { "name": "2.4.1.0", "tag_name": "2.4.1.0", - "published_at": "2024-09-23T05:38:06Z", + "published_at": "2024-09-23T05:50:16Z", "installation_critical": false, "assets": [ { "name": "Ziti.Desktop.Edge.Client-2.4.1.0.exe", - "browser_download_url": "http://localhost:8000/release-streams/local/2.4.1.0/Ziti.Desktop.Edge.Client-2.4.1.0.exe" + "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.4.1.0/Ziti.Desktop.Edge.Client-2.4.1.0.exe" } ] } From c58e11777db49e569d911ee30b193f5e7ae3904e Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:02:46 -0400 Subject: [PATCH 05/12] update gh workflow --- .github/workflows/installer.build.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/installer.build.yml b/.github/workflows/installer.build.yml index 70c294b5b..395bb0658 100644 --- a/.github/workflows/installer.build.yml +++ b/.github/workflows/installer.build.yml @@ -43,12 +43,12 @@ jobs: - name: Set up environment variable run: echo "ZITI_EDGE_TUNNEL_BUILD=${{ github.event.inputs.zet }}" >> $GITHUB_ENV - name: Git Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: persist-credentials: false - - uses: microsoft/setup-msbuild@v1 + - uses: microsoft/setup-msbuild@v2 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 - if: github.event_name == 'push' run: echo "GIT_BRANCH=$($env:GITHUB_REF.replace('refs/heads/', ''))" >> $env:GITHUB_ENV - if: github.event_name == 'pull_request' @@ -71,7 +71,7 @@ jobs: run: choco install advanced-installer --version="$(cat .\adv-inst-version)" --yes - name: Setting up VERSION env var run: echo "VERSION=$(cat .\version)" >> $env:GITHUB_ENV - - uses: nuget/setup-nuget@v1 + - uses: nuget/setup-nuget@v2 with: nuget-api-key: ${{ secrets.NuGetAPIKey }} nuget-version: '5.8.1' @@ -94,18 +94,13 @@ jobs: ziti_ci_gpg_key_id: ${{ secrets.ZITI_CI_GPG_KEY_ID }} run: powershell -File .\Installer\build.ps1 - name: Upload installer onto job - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ZitiDesktopEdgeClient-${{ env.VERSION }} path: "Installer/Output/Ziti Desktop Edge Client-*" - - name: Upload update json to job - uses: actions/upload-artifact@v2 - with: - name: ZitiDesktopEdgeClient-${{ env.VERSION }} - path: ${{ env.VERSION }}.json - name: Job Failure - Upload the signing log job if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: SigningLogs path: '**/*AWSSigner.log*' From 6eb20d3bc9ec11a1ee4c45b3e6fe4f8e5cbbe258 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:50:56 -0400 Subject: [PATCH 06/12] merge origin/main --- .editorconfig | 10 +- .github/workflows/dependabot.yml | 12 + AWSSigner.NET/AWSSigner.NET.csproj | 4 +- AWSSigner.NET/App.config | 100 + AWSSigner.NET/Program.cs | 396 +- AWSSigner.NET/packages.config | 2 +- DesktopEdge/App.config | 2 +- DesktopEdge/App.xaml.cs | 158 +- DesktopEdge/Assets/Images/ext-auth-needed.png | Bin 0 -> 20988 bytes DesktopEdge/MainWindow.xaml.cs | 3585 +++++++++-------- DesktopEdge/Models/FilterData.cs | 74 +- DesktopEdge/Models/MFA.cs | 56 +- DesktopEdge/Models/MessageCount.cs | 62 +- DesktopEdge/Models/UIModel.cs | 39 +- DesktopEdge/Models/ViewState.cs | 34 +- DesktopEdge/Models/ZitiIdentity.cs | 313 +- DesktopEdge/Models/ZitiService.cs | 546 +-- DesktopEdge/Native/NativeMethods.cs | 91 +- DesktopEdge/Native/WinAPI.cs | 132 +- DesktopEdge/Properties/AssemblyInfo.cs | 110 +- DesktopEdge/Utils/UIUtils.cs | 79 +- .../Views/Controls/StyledButton.xaml.cs | 176 +- DesktopEdge/Views/Controls/Toggler.xaml.cs | 150 +- .../Views/ItemRenderers/Filter.xaml.cs | 212 +- .../Views/ItemRenderers/IdentityItem.xaml | 2 +- .../Views/ItemRenderers/IdentityItem.xaml.cs | 684 ++-- .../Views/ItemRenderers/MenuEditItem.xaml.cs | 120 +- .../ItemRenderers/MenuEditSearch.xaml.cs | 140 +- .../ItemRenderers/MenuEditToggle.xaml.cs | 156 +- .../ItemRenderers/MenuIdentityItem.xaml.cs | 140 +- .../Views/ItemRenderers/MenuItem.xaml.cs | 112 +- .../Views/ItemRenderers/SubMenuItem.xaml.cs | 76 +- .../Views/ItemRenderers/SubOptionItem.xaml.cs | 100 +- DesktopEdge/Views/Screens/Debugging.xaml.cs | 34 +- .../Views/Screens/IdentityDetails.xaml.cs | 1076 ++--- DesktopEdge/Views/Screens/MFAScreen.xaml.cs | 634 +-- DesktopEdge/Views/Screens/MainMenu.xaml | 2 +- DesktopEdge/Views/Screens/MainMenu.xaml.cs | 1498 +++---- DesktopEdge/ZitiDesktopEdge.csproj | 1 + Installer/build.ps1 | 1 - .../DataStructures/DataStructures.cs | 224 +- .../Properties/AssemblyInfo.cs | 34 +- .../Server/EventRegistry.cs | 34 +- ZitiDesktopEdge.Client/Server/IPCServer.cs | 68 +- .../Server/ServiceActions.cs | 30 +- .../ServiceClient/AbstractClient.cs | 86 +- .../ServiceClient/DataClient.cs | 68 +- .../ServiceClient/MonitorClient.cs | 141 +- ZitiDesktopEdge.Client/Utility/GithubAPI.cs | 100 +- .../Utility/UpgradeSentinel.cs | 84 +- ZitiDesktopEdge.sln | 3 + ZitiUpdateService/Program.cs | 150 +- ZitiUpdateService/ProjectInstaller.cs | 50 +- ZitiUpdateService/Properties/AssemblyInfo.cs | 34 +- ZitiUpdateService/UpdateService.cs | 2050 +++++----- ZitiUpdateService/Utils.cs | 39 +- .../ZitiUpdateService-log.config | 4 +- ZitiUpdateService/checkers/FilesystemCheck.cs | 92 +- ZitiUpdateService/checkers/GithubCheck.cs | 336 +- .../checkers/PeFile/SignedFileStructs.cs | 37 +- .../checkers/PeFile/SignedFileValidator.cs | 53 +- .../checkers/PeFile/StructHelper.cs | 34 +- .../checkers/PeFile/Win32Crypto.cs | 34 +- ZitiUpdateService/checkers/UpdateCheckers.cs | 54 +- ZitiUpdateService/utils/Settings.cs | 244 +- ZitiUpgradeSentinel/Program.cs | 304 +- .../Properties/AssemblyInfo.cs | 34 +- build-test-release.ps1 | 2 - release-notes.md | 116 + release-streams/ctrlha-alpha.json | 10 +- release-streams/latest.json | 10 +- release-streams/stable.json | 10 +- setup-mfa-test.ps1 | 75 +- update-versions.ps1 | 8 +- 74 files changed, 8064 insertions(+), 7707 deletions(-) create mode 100644 .github/workflows/dependabot.yml create mode 100644 DesktopEdge/Assets/Images/ext-auth-needed.png diff --git a/.editorconfig b/.editorconfig index a2713da48..653e14893 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,10 @@ csharp_style_expression_bodied_accessors = true:silent csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_local_functions = false:silent csharp_indent_labels = one_less_than_current +csharp_new_line_before_open_brace = none +csharp_new_line_before_else = false +csharp_new_line_before_catch = false +csharp_new_line_before_finally = false [*.{cs,vb}] #### Naming styles #### @@ -75,7 +79,7 @@ tab_width = 4 indent_size = 4 end_of_line = crlf dotnet_style_collection_initializer = true:suggestion -indent_style = tab +indent_style = space dotnet_style_prefer_simplified_boolean_expressions = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:silent dotnet_style_prefer_conditional_expression_over_return = true:silent @@ -93,3 +97,7 @@ dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent dotnet_style_allow_multiple_blank_lines_experimental = true:silent dotnet_style_allow_statement_immediately_after_block_experimental = true:silent dotnet_code_quality_unused_parameters = all:suggestion + +[*.xaml] +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 000000000..465aa07cc --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +name: dependabot + +version: 2 +updates: + - package-ecosystem: "" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/AWSSigner.NET/AWSSigner.NET.csproj b/AWSSigner.NET/AWSSigner.NET.csproj index e89120770..f8ccb8004 100644 --- a/AWSSigner.NET/AWSSigner.NET.csproj +++ b/AWSSigner.NET/AWSSigner.NET.csproj @@ -118,8 +118,8 @@ ..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll - - ..\packages/System.Text.Json.8.0.4/lib/net462/System.Text.Json.dll + + ..\packages\System.Text.Json.8.0.5\lib\net462\System.Text.Json.dll True diff --git a/AWSSigner.NET/App.config b/AWSSigner.NET/App.config index adf9a72f8..a659cf1e3 100644 --- a/AWSSigner.NET/App.config +++ b/AWSSigner.NET/App.config @@ -30,6 +30,106 @@ that is bundled with the nuget package under the tools folder. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AWSSigner.NET/Program.cs b/AWSSigner.NET/Program.cs index 89c63ea4d..29cec26ba 100644 --- a/AWSSigner.NET/Program.cs +++ b/AWSSigner.NET/Program.cs @@ -27,202 +27,202 @@ limitations under the License. namespace AWSSigner { - class Program { - private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); - private static bool showDebugOutput = "TRUE" == ("" + Environment.GetEnvironmentVariable("AWSSIGNER_DEBUG")).ToUpper(); - static void Main(string[] args) { - var asm = Assembly.GetExecutingAssembly(); - var curdir = Path.GetDirectoryName(System.AppContext.BaseDirectory); - var config = new LoggingConfiguration(); - // Targets where to log to: File and Console - var logfile = new FileTarget("logfile") { - FileName = $"AWSSigner.log", - ArchiveEvery = FileArchivePeriod.Day, - ArchiveNumbering = ArchiveNumberingMode.Rolling, - MaxArchiveFiles = 7, - AutoFlush = true, - Layout = "[${date:universalTime=true:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", - }; - var logconsole = new ConsoleTarget("logconsole"); - - // Rules for mapping loggers to targets - config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole); - config.AddRule(LogLevel.Info, LogLevel.Fatal, logfile); - - // Apply config - LogManager.Configuration = config; - - Logger.Info("========================= signing started ========================="); - Logger.Info("logger initialized"); - Logger.Info(" - name : {0}", asm.GetName()); - Logger.Info(" - path : {0}", curdir); - Logger.Info(" - args : {0}", String.Join(",", args)); - Logger.Info("========================================================================"); - - if (args[0].Contains(AppDomain.CurrentDomain.FriendlyName)) { - Logger.Debug("args[0] contains the AppDomain.CurrentDomain.FriendlyName, must be using dotnet run?"); - args = args.Skip(1).ToArray(); - } - - bool argsValid = true; - if (args.Length < 1) { - Logger.Info("Usage: signfile \n"); - Logger.Info("ERROR: provide target file to sign and cert to use as arguments"); - argsValid = false; - } - - bool envVarsExist = VerifyEnvVar("AWS_KEY_ID"); - envVarsExist = VerifyEnvVar("AWS_ACCESS_KEY_ID"); - envVarsExist = VerifyEnvVar("AWS_REGION"); - envVarsExist = VerifyEnvVar("AWS_SECRET_ACCESS_KEY"); - envVarsExist = VerifyEnvVar("SIGNING_CERT"); - - if (!argsValid || !envVarsExist) { - return; - } - - bool filesExist = true; - string fileToSign = String.Join(",", args); //have to join all the args when spaces are used??? - Logger.Info($"File to sign: '{fileToSign}'"); - if (!File.Exists(fileToSign)) { - Logger.Info($"File to sign doesn't exist: {fileToSign}"); - filesExist = false; - } - string certToUse = Environment.GetEnvironmentVariable("SIGNING_CERT"); - if (!File.Exists(certToUse)) { - Logger.Info($"Cert to use doesn't exist : {certToUse}"); - filesExist = false; - } - - if (!filesExist) { return; } - - string exeAbsPath = Path.GetFullPath(fileToSign); - string loc = Path.GetDirectoryName(exeAbsPath); - - string signToolPath = GetFullPath("signtool.exe"); - string awsKeyId = Environment.GetEnvironmentVariable("AWS_KEY_ID"); - - if (!File.Exists(signToolPath)) { - string signToolPathEnv = Environment.GetEnvironmentVariable("SIGNTOOL_PATH"); - if (!File.Exists(signToolPathEnv)) { - Logger.Info($"ERROR: Signtool not found on path and SIGNTOOL_PATH environment variable not set or file doesn't exist: {signToolPathEnv}!"); - return; - } else { - Logger.Info($"Using signtool found via environment variable at: {signToolPathEnv}"); - signToolPath= signToolPathEnv; - } - } - - Logger.Info($"Using signtool : {signToolPath}"); - Logger.Info($"Using cert : {certToUse}"); - Logger.Info($"Signing file : {fileToSign}"); - - Logger.Debug("----- signFile: producing digest to send to AWS KMS -----"); - RunProcess(signToolPath, $"sign /dg {loc} /fd sha256 /f \"{certToUse}\" \"{exeAbsPath}\""); - Logger.Info(" - digest file produced, sending to KMS for signing"); - - byte[] tosign = Convert.FromBase64String(File.ReadAllText($"{exeAbsPath}.dig")); - Logger.Debug("----- signFile: sending digest to AWS KMS for signing. -----"); - string signature = SignWithAwsKms(awsKeyId, tosign); - File.WriteAllText($"{exeAbsPath}.dig.signed", signature); - Logger.Info(" - digest signed, attaching signature"); - - Logger.Debug($"----- signature len: {signature.Length} ----"); - Logger.Debug("----- done signing digest -----"); - Logger.Debug("----- signFile: adding signature -----"); - RunProcess(signToolPath, $"sign /di \"{loc}\" \"{exeAbsPath}\""); - Logger.Info(" - signature attached, timestamping"); - - Logger.Debug("----- signFile: adding timestamp -----"); - RunProcess(signToolPath, $"timestamp /tr http://timestamp.digicert.com /td sha256 \"{exeAbsPath}\""); - Logger.Info(" - timestamped, verifying"); - - RunProcess(signToolPath, $"verify /pa \"{exeAbsPath}\""); - Logger.Info(" - verified, removing any files leftover from signing"); - DeleteFile($"{exeAbsPath}.dig"); - DeleteFile($"{exeAbsPath}.dig.signed"); - DeleteFile($"{exeAbsPath}.p7u"); - - Logger.Info($"process complete. signed: {fileToSign}\n"); - } - - static void RunProcess(string fileName, string arguments) { - Logger.Debug($"RunProcess Args: {fileName} {arguments}"); - var process = new Process { - StartInfo = new ProcessStartInfo { - FileName = fileName, - Arguments = arguments, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - } - }; - - process.Start(); - process.WaitForExit(); - - string output = process.StandardOutput.ReadToEnd(); - string error = process.StandardError.ReadToEnd(); - - if (!string.IsNullOrEmpty(output)) { - Logger.Debug(output); - } - - if (!string.IsNullOrEmpty(error)) { - Logger.Error(error); - } - - if (process.ExitCode != 0) { - throw new Exception($"Process exited with code {process.ExitCode}: {error}"); - } - } - - static string SignWithAwsKms(string keyId, byte[] digest) { - var kmsClient = new AmazonKeyManagementServiceClient(); - var request = new SignRequest { - KeyId = keyId, - Message = new MemoryStream(digest), - MessageType = MessageType.DIGEST, - SigningAlgorithm = SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_256 - }; - - var response = kmsClient.SignAsync(request).Result; - - return Convert.ToBase64String(response.Signature.ToArray()); - } - - static void DeleteFile(string path) { - if (File.Exists(path)) { - File.Delete(path); - } - } - - public static bool ExistsOnPath(string fileName) { - return GetFullPath(fileName) != null; - } - - public static string GetFullPath(string fileName) { - if (File.Exists(fileName)) - return Path.GetFullPath(fileName); - - string values = Environment.GetEnvironmentVariable("PATH"); - if (values == null) { return null; } - foreach (var path in values.Split(Path.PathSeparator)) { - var fullPath = Path.Combine(path, fileName); - if (File.Exists(fullPath)) - return Path.GetFullPath(fullPath); - } - return null; - } - - public static bool VerifyEnvVar(string envVar) { - var val = Environment.GetEnvironmentVariable(envVar); - if (string.IsNullOrEmpty(val)) { - Logger.Info($"ERROR: Environment variable must be set: {envVar}"); - return false; - } - return true; - } - } + class Program { + private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); + private static bool showDebugOutput = "TRUE" == ("" + Environment.GetEnvironmentVariable("AWSSIGNER_DEBUG")).ToUpper(); + static void Main(string[] args) { + var asm = Assembly.GetExecutingAssembly(); + var curdir = Path.GetDirectoryName(System.AppContext.BaseDirectory); + var config = new LoggingConfiguration(); + // Targets where to log to: File and Console + var logfile = new FileTarget("logfile") { + FileName = $"AWSSigner.log", + ArchiveEvery = FileArchivePeriod.Day, + ArchiveNumbering = ArchiveNumberingMode.Rolling, + MaxArchiveFiles = 7, + AutoFlush = true, + Layout = "[${date:universalTime=true:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", + }; + var logconsole = new ConsoleTarget("logconsole"); + + // Rules for mapping loggers to targets + config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole); + config.AddRule(LogLevel.Info, LogLevel.Fatal, logfile); + + // Apply config + LogManager.Configuration = config; + + Logger.Info("========================= signing started ========================="); + Logger.Info("logger initialized"); + Logger.Info(" - name : {0}", asm.GetName()); + Logger.Info(" - path : {0}", curdir); + Logger.Info(" - args : {0}", String.Join(",", args)); + Logger.Info("========================================================================"); + + if (args[0].Contains(AppDomain.CurrentDomain.FriendlyName)) { + Logger.Debug("args[0] contains the AppDomain.CurrentDomain.FriendlyName, must be using dotnet run?"); + args = args.Skip(1).ToArray(); + } + + bool argsValid = true; + if (args.Length < 1) { + Logger.Info("Usage: signfile \n"); + Logger.Info("ERROR: provide target file to sign and cert to use as arguments"); + argsValid = false; + } + + bool envVarsExist = VerifyEnvVar("AWS_KEY_ID"); + envVarsExist = VerifyEnvVar("AWS_ACCESS_KEY_ID"); + envVarsExist = VerifyEnvVar("AWS_REGION"); + envVarsExist = VerifyEnvVar("AWS_SECRET_ACCESS_KEY"); + envVarsExist = VerifyEnvVar("SIGNING_CERT"); + + if (!argsValid || !envVarsExist) { + return; + } + + bool filesExist = true; + string fileToSign = String.Join(",", args); //have to join all the args when spaces are used??? + Logger.Info($"File to sign: '{fileToSign}'"); + if (!File.Exists(fileToSign)) { + Logger.Info($"File to sign doesn't exist: {fileToSign}"); + filesExist = false; + } + string certToUse = Environment.GetEnvironmentVariable("SIGNING_CERT"); + if (!File.Exists(certToUse)) { + Logger.Info($"Cert to use doesn't exist : {certToUse}"); + filesExist = false; + } + + if (!filesExist) { return; } + + string exeAbsPath = Path.GetFullPath(fileToSign); + string loc = Path.GetDirectoryName(exeAbsPath); + + string signToolPath = GetFullPath("signtool.exe"); + string awsKeyId = Environment.GetEnvironmentVariable("AWS_KEY_ID"); + + if (!File.Exists(signToolPath)) { + string signToolPathEnv = Environment.GetEnvironmentVariable("SIGNTOOL_PATH"); + if (!File.Exists(signToolPathEnv)) { + Logger.Info($"ERROR: Signtool not found on path and SIGNTOOL_PATH environment variable not set or file doesn't exist: {signToolPathEnv}!"); + return; + } else { + Logger.Info($"Using signtool found via environment variable at: {signToolPathEnv}"); + signToolPath = signToolPathEnv; + } + } + + Logger.Info($"Using signtool : {signToolPath}"); + Logger.Info($"Using cert : {certToUse}"); + Logger.Info($"Signing file : {fileToSign}"); + + Logger.Debug("----- signFile: producing digest to send to AWS KMS -----"); + RunProcess(signToolPath, $"sign /dg {loc} /fd sha256 /f \"{certToUse}\" \"{exeAbsPath}\""); + Logger.Info(" - digest file produced, sending to KMS for signing"); + + byte[] tosign = Convert.FromBase64String(File.ReadAllText($"{exeAbsPath}.dig")); + Logger.Debug("----- signFile: sending digest to AWS KMS for signing. -----"); + string signature = SignWithAwsKms(awsKeyId, tosign); + File.WriteAllText($"{exeAbsPath}.dig.signed", signature); + Logger.Info(" - digest signed, attaching signature"); + + Logger.Debug($"----- signature len: {signature.Length} ----"); + Logger.Debug("----- done signing digest -----"); + Logger.Debug("----- signFile: adding signature -----"); + RunProcess(signToolPath, $"sign /di \"{loc}\" \"{exeAbsPath}\""); + Logger.Info(" - signature attached, timestamping"); + + Logger.Debug("----- signFile: adding timestamp -----"); + RunProcess(signToolPath, $"timestamp /tr http://timestamp.digicert.com /td sha256 \"{exeAbsPath}\""); + Logger.Info(" - timestamped, verifying"); + + RunProcess(signToolPath, $"verify /pa \"{exeAbsPath}\""); + Logger.Info(" - verified, removing any files leftover from signing"); + DeleteFile($"{exeAbsPath}.dig"); + DeleteFile($"{exeAbsPath}.dig.signed"); + DeleteFile($"{exeAbsPath}.p7u"); + + Logger.Info($"process complete. signed: {fileToSign}\n"); + } + + static void RunProcess(string fileName, string arguments) { + Logger.Debug($"RunProcess Args: {fileName} {arguments}"); + var process = new Process { + StartInfo = new ProcessStartInfo { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + process.WaitForExit(); + + string output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + + if (!string.IsNullOrEmpty(output)) { + Logger.Debug(output); + } + + if (!string.IsNullOrEmpty(error)) { + Logger.Error(error); + } + + if (process.ExitCode != 0) { + throw new Exception($"Process exited with code {process.ExitCode}: {error}"); + } + } + + static string SignWithAwsKms(string keyId, byte[] digest) { + var kmsClient = new AmazonKeyManagementServiceClient(); + var request = new SignRequest { + KeyId = keyId, + Message = new MemoryStream(digest), + MessageType = MessageType.DIGEST, + SigningAlgorithm = SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_256 + }; + + var response = kmsClient.SignAsync(request).Result; + + return Convert.ToBase64String(response.Signature.ToArray()); + } + + static void DeleteFile(string path) { + if (File.Exists(path)) { + File.Delete(path); + } + } + + public static bool ExistsOnPath(string fileName) { + return GetFullPath(fileName) != null; + } + + public static string GetFullPath(string fileName) { + if (File.Exists(fileName)) + return Path.GetFullPath(fileName); + + string values = Environment.GetEnvironmentVariable("PATH"); + if (values == null) { return null; } + foreach (var path in values.Split(Path.PathSeparator)) { + var fullPath = Path.Combine(path, fileName); + if (File.Exists(fullPath)) + return Path.GetFullPath(fullPath); + } + return null; + } + + public static bool VerifyEnvVar(string envVar) { + var val = Environment.GetEnvironmentVariable(envVar); + if (string.IsNullOrEmpty(val)) { + Logger.Info($"ERROR: Environment variable must be set: {envVar}"); + return false; + } + return true; + } + } } \ No newline at end of file diff --git a/AWSSigner.NET/packages.config b/AWSSigner.NET/packages.config index 0fd957c3b..b597eff06 100644 --- a/AWSSigner.NET/packages.config +++ b/AWSSigner.NET/packages.config @@ -26,7 +26,7 @@ - + \ No newline at end of file diff --git a/DesktopEdge/App.config b/DesktopEdge/App.config index a353d6d8d..37dfe4b8f 100644 --- a/DesktopEdge/App.config +++ b/DesktopEdge/App.config @@ -1,7 +1,7 @@ - + diff --git a/DesktopEdge/App.xaml.cs b/DesktopEdge/App.xaml.cs index 8bc2f2814..964bcf245 100644 --- a/DesktopEdge/App.xaml.cs +++ b/DesktopEdge/App.xaml.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; @@ -32,78 +32,78 @@ limitations under the License. using ZitiDesktopEdge.Utility; namespace ZitiDesktopEdge { - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application { + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application { private const string NamedPipeName = "ZitiDesktopEdgePipe"; - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - private static Mutex _mutex = null; + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private static Mutex _mutex = null; - protected override void OnSessionEnding(SessionEndingCancelEventArgs e) { - base.OnSessionEnding(e); - } + protected override void OnSessionEnding(SessionEndingCancelEventArgs e) { + base.OnSessionEnding(e); + } - protected override void OnStartup(StartupEventArgs e) { - UpgradeSentinel.RemoveUpgradeSentinelExe(); - try { - Current.Properties["ZDEWViewState"] = new ZDEWViewState(); + protected override void OnStartup(StartupEventArgs e) { + UpgradeSentinel.RemoveUpgradeSentinelExe(); + try { + Current.Properties["ZDEWViewState"] = new ZDEWViewState(); - const string appName = "Ziti Desktop Edge"; + const string appName = "Ziti Desktop Edge"; - bool createdNew; + bool createdNew; - _mutex = new Mutex(true, appName, out createdNew); + _mutex = new Mutex(true, appName, out createdNew); - if (!createdNew) { - using (var client = new NamedPipeClientStream(NamedPipeName)) { - logger.Info("Another instance exists. Attempting to notify it to open"); - try { - client.Connect(1000); - } catch { - return; - } + if (!createdNew) { + using (var client = new NamedPipeClientStream(NamedPipeName)) { + logger.Info("Another instance exists. Attempting to notify it to open"); + try { + client.Connect(1000); + } catch { + return; + } - if (!client.IsConnected) - return; + if (!client.IsConnected) + return; - using (StreamWriter writer = new StreamWriter(client)) { - writer.Write("showscreen"); - writer.Flush(); - } - } - Application.Current.Shutdown(); - } else { + using (StreamWriter writer = new StreamWriter(client)) { + writer.Write("showscreen"); + writer.Flush(); + } + } + Application.Current.Shutdown(); + } else { #pragma warning disable 4014 //This async method lacks 'await' - StartServer(); + StartServer(); #pragma warning restore 4014 //This async method lacks 'await' - } - } catch (Exception ex) { - logger.Error($"OnStartup FAILED unexpectedly. Exiting", ex); - Application.Current.Shutdown(); - } - } - - async public Task StartServer() { - logger.Debug("Starting IPC server to listen for other instances of the app"); - while (true) { - string text; - using (var server = new NamedPipeServerStream(NamedPipeName)) { - await server.WaitForConnectionAsync(); - logger.Debug("Another instance opened and connected."); - using (StreamReader reader = new StreamReader(server)) { - text = await reader.ReadToEndAsync(); - } - } - - logger.Debug("received: {0}. Calling OnReceivedString", text); - OnReceivedString(text); - } - } - - public event Action ReceiveString; - protected virtual void OnReceivedString(string text) => ReceiveString?.Invoke(text); - - } + } + } catch (Exception ex) { + logger.Error($"OnStartup FAILED unexpectedly. Exiting", ex); + Application.Current.Shutdown(); + } + } + + async public Task StartServer() { + logger.Debug("Starting IPC server to listen for other instances of the app"); + while (true) { + string text; + using (var server = new NamedPipeServerStream(NamedPipeName)) { + await server.WaitForConnectionAsync(); + logger.Debug("Another instance opened and connected."); + using (StreamReader reader = new StreamReader(server)) { + text = await reader.ReadToEndAsync(); + } + } + + logger.Debug("received: {0}. Calling OnReceivedString", text); + OnReceivedString(text); + } + } + + public event Action ReceiveString; + protected virtual void OnReceivedString(string text) => ReceiveString?.Invoke(text); + + } } \ No newline at end of file diff --git a/DesktopEdge/Assets/Images/ext-auth-needed.png b/DesktopEdge/Assets/Images/ext-auth-needed.png new file mode 100644 index 0000000000000000000000000000000000000000..cafc1a74963e39044648f3ae1b4e28ea89ec5fc2 GIT binary patch literal 20988 zcmb5WbyO7I^8mVpAi@fQfUIBEFmGWbn^iu6bYr5lJ4%55KvJN>4v2w zq*;_&N#8|3-_P&8KcDAt_UzudGjnHdO!`V&Qw4g3?g|6~fg)5Dbs-RZ)WzRrBJj(h zOa3tUz;n}8k%v_D)31Xsgw}Ezau7&$H0i0?CGefZS=GP|0=e38@rT#tRP+=A(TPPU z%02wYWP4Vk?CIx&!-asgm_ZisFIz!A^yO6&lJ3hS;2(oH`*K{cKH&|XO zCk89Ck+P5)oE`W^k&n3bPtTszY}Azae+c?%ULaWSwbSZptamoQ;0y@MTH8}Vnh&;y z7JlsKb}amq2<;GaVz?S`GHW^C=6Cnm@44;yqwniKIeP8zWkWAuv-7rEyvRcZO=RfL zPg6SW7#8leiDQZHkeH{+7Jw|8Q>#FG~nU7??A9KY#94(v1V+Tl?@E(rH~B0T0aJuO>~{QH45+Lajc9j5lEBYy}* zFBpgqxgrzL$0dCeRYAug0l97!1fft4Cs3P^u#-b@g&YT!E(GmsB2uA)&1MR>}qUZ234L z6kmw&OsP0{*vc1;?qBjTZh^cfx_~MY4Zd$;wL9C=RUqU*0O6fthLjbsRtlh!WA*Br z;rM~nKnZ$78=c8CSB&<4-sfY7RHXtAZUy{q)zY&%>@(~txb1KWLJB7G9qzMb=Jjey zh3^;OO@06bs|QRD5LX<<;=LS*Af%H3B!^^I;NAzfEoF!)8xW$>no^7A-ht6H-g|1m z&f~rsF>6A&8FAHfK;sdna+rhtAJrD2MmS+0DPTlEcOd4zvh&H?IlwVfHGWPsA{gTl0LP+jsucr76frl4B0lKe6e|Bm$2#uKx;7HzDh-5 zR7hQ2p@H`vV7R;K)V@b@xx#`NAHoZm)Nhw;osApjU~bg3yl>Qk9|(|`p8d+!lCGlP z8;0-!jhNnMa**L@u5iHn#t0*XY;KDAEk6Gqzt-GvDbN=%%IDy;Oi9j%yAY4411SV| zfp%-6X?{K+z+P>n0pc7D#Sc_MH)N{4Y5aFiS@e_LSHy{I2!bl0V9W;BgGn9uyPpU+E=oFKXYEn=;+?4|mnEg9bfZ`L2tGW5S|kM?}4 z`A7Zp8-j;Bw|)H5iN%@fREqk^an^Wwra=(#!;2$4{nm*qYhDY2jaM6 zZ8787`$6uQKq$V=)>Q8FT+Ike?;SM`RLb7U9C^>__qSbEqT6(42()LWkHwh8@yO$@ zB%w-@EFKu4cJj(q)W>!-@9dXZs7ItOBBko1TMN3&@;=&gW8|^pYRx*{hgRvp4SNdN zOB)HpRU;{8z4>aK1U?S9jK+%562qy~y!7Fq7g28A1Ek+>0gPXaqmx%IHKkg_qY){T z_W_uC%bj>5hnvX&P!gW5g{)efqm{i4zsx*{GS{;+%m;9sX|+`*ys~ zq$vAWHJz+0FhUC1d>UI$t3owSl&*Q0r#mux6C32UKr8<;YI4UYCXiYS>e}S5rfZrhRub<&W1K7JWG_ zeyvY2U%*I0*)}-dqQ?XfoGTBhxSw>DP?q>Stb4f)b@)>>Wp{6MJz32Ol|n-=J+F#!CU$hRvb@N zhp}sBD&$gIOjnGa2EE^}qsYRs^xe1RMA{w7@XGvZN2;n$_-b{@b+)tL zZ6PoW)ZJr7U1&oaJj5ik(4|?$`p+8S40x=?t6EalbU6+NSXqa4>G=qEJ|kFy=UY^} zXY-fy!R)=1r_i`sIa3=pc56D5(SD+h53cjo2^m+88*NQA_1|>-Ujda({I|S%E z>b*qISx|RXr8IBGi5$Ex@=|PNm#PeVpryI;(p0do)OCB9fcnXJT2C3Nx$3wO3yd(t zH7Tu$G+^LiwQ;L({f51gjeRf1pFC`T4ps{MdPgZbOPbJ1WKTyo~ReG<``8TSdB0)_gW&%v}@J zuSCxs*Oe>79V@kd()Eu<+7&GH)(h{bXR*lO(ya;^dHKiJ>mM5`jPMD_%EK?aBWR~g zR?~~Ulu8$8Ry!3o%zEp1T71Iatky+DmBW*~wkPSju60i`4^kUH^esbI%t?+Vn$Puk zwhI2zX6=qSfTy4b?XIB?R)(h|3GFG?UaZ7&lep4OCB{Fru?Y=-M#iu@r&g%#%iC|P zvp%QyYs0H7VH_($kX|Lno;OEd-aMRklY;`c&RO(ZAQ?^S=jrv_L9Uc)Y_EfTS=i@| zQxWW~KQH~#KjLl8(g)*iCB;{;dkqD4_5Dg5jdTUAm*grISiLatb_k%LQ>$6k1|5^>0JI+Rh9f7!F3JSQv#yRaQKa z;u9XUUbAd|5i)iVld~ClQf9a|Gkz*tk1OM_K-bE-tG?69Aq^^q7#w(UiEa=7>Nj;l^;;rwj?F{D* zGC}*sjILqCj6*ykp^factmn%YDahQ4ZO#yb=xdw5Idfq4lRl7;}XJ*s~V zBP=&^a;TS~*u5z7mH6N*(NK&YZ0t>FZDMn`ulL4l%~AIUSzedcLVZ%ywls%s$+Z|R zY)C&4jF%K9!!W=U2`)C^TQk8_S`~I%qxitK^$$X}0_Nd_+m2sf`)tfwZ(JiUH?Ed@ znD?@7*1J89$|{@$^9(c5h$Pp^w6b)}A7v2v_zN zKiY1MQA^vDYPsuqamK+Sv(YT2j2VqaIR#t>934icfj_yA zn(K6HJYT>U>(8PJ!m4&#NnnaBDG|vo=To}nnT=S=I~AZ%|Ye% zhHy+>sFFn-z#HRDxlV^Xn-bQsIH5AtOd&3?v)?AvAP*8TnpT>R+q8QG4>J`nwm!NR z?lWp60g*-3$AwWI%=i>ADip*#vUweo@8%`eo4LMa=iR(_o6)o-adPO^sk0MzEZx`{Q2h@jfVSC39dxb+A?Sov1jBn!`@20BG{`SMV1m>)IBO9nYSjf(Q@k=2P(BAA7d{v+%Hm*PVteb zI!;flInLXvR|}RzDU!Y?`DW(o&r*KX$<92tSq9wH?Ja`{s5!C!Cq^wpiy4z>E)gbGp1c)1pC_3;vv^M>Ga6h+jOh787L@BV9L;Sd)clw8H}UPFzD~cho(*0tSC(h)k9`_km>O%R zGFtcd%_KYJ?9bdKj4M z3N3OimZKOpv@1E=V-M=W`s=y$YURol5ibtTe}-C&BsVB}->NGqH1;wYWMbfDw8dz* zv{=08sgtR+(<-wH(wB>4pGCaxsY}1==Cm=~RI)tDK)$cl6@{4csP^rJUw;d=06?WR zGlqxBm#KMEN2JRG~yzEZ%$sEZ;S7)k@bj?7SmEOq5(Tsb7>4_{n9 zsp?R_4ErQcS20qRE+OZVPH8kzF{!0hJ^$|eG{e%MGrbgzWfikMlW~Aii#aubl<{BF z(&Wp{h5B98>3>W8GpG~LwE^hua ztCpO6fyJJJw0hTC=0Lf*;j=j1?qWAniv0T@tNtlp7csP(Rdn8B0EhaQx#$Gj6I4<7 zBb&w!Fv>?4_QAU`6>84n7;#c8HeAo2I7E?T_c(B2V4K?KoIzgIRIlg60wX)p4aO3GkNryEpL4m~KrX07<^!qROztl5~ zFBnaOxiJzv)mKT0D{^gwbEKemu0zU_&PHRt2xN9327 zOer>|>$ZJcLQiK$C9-YyLU~@R{$+4qRVm4ea^>vQ%)^_HUd-*qHAtsQ=Spv`iP;x0 zNPcPvI9ZeHs^`_KmgD=BD0xziw-a9XbhiG1XS3;MHs{`tQ0bPoj-pokl;3M@xH78@ zM+!Mah27&a^C0~&OUy};*T?$JGUd{~JTwcP_~_0jzxm$%^!-_7-tdnl1FNF@+nCEe zG5euaa(s&Ab$&O@y)`@9Dk6o4jWjsYM0!<*yhgnkQ2w_psT_+}N9)NG%|gR3j)AoA zSY}qE=E+hMJUx=LEP9YFx5+R$R0KEd^%&OJ(ET!*cumA0x4=NU~T)@tJomEAv0Fs;4i zb+7)>c(WI#+HA%A?J<_jyCOG~@(Oz>xP(59OKtYe5yZK?IgLy5mY-8-#ol~doZ`23 z>txWwfsWO4I}CV1yeMplguz0ei42$FI*ruLQh%6M*GVz-(F*G{#ab_~w88hL{xA+N zx*9qA_qPVVTg_cjlL|`?(Gsn@ck(WeQj{qzzcB6M0jAhx+-|2oOHY>f|ykU-0Du2*JpZZ*5O zj;JH`Z19KmPQJL6adzLq?9#Cgc#XI+8W>X!RLbUfCKy`>De4?0v9-Aj(0e#(Er;=p z$K{${+qZce0&jfocv0V^vht?V}*k1|)T9=3hSW!H1lGXquyzv&E# zwDwappQroXc;(qA;#h!NJ5=@cf=HXh4VzBLcansne5L~N$Z>^kj ze#>nHYojW@`2wHgjff!OdKff^mE+!&|Cfz?v#5`5tp_B{GC%Wvj_v{YH1Hf!#m}ldly?zr&I9*w>t)%X|h^^`tocXx+h^B zWTUEOETvca*nxB9uGHuQLAt%cmGS9QQJ)b>4uw{FE@P30VQ+|A9H*GY2YVhQ`QRES zGbTo~?{r*WL-~V{!YU8XFoj2{-8;01vXX9}9E*kN=F9;Ug@V?g_jl2`sfv5-Z>|9#Ja ztH|geNe6Xfu3L7}g6N^QkqWuLmUN@T+c09;z6TGZ)3FTLvq34CBEHYDZg2pHo*fri zX`y<6O2A`U!j;?Qg)paxLvqY8PX z7s<_B<__TGdEkQDKAlOG6@L0M8*gjzL^Exqr^G@JihmI(;q~E8$}zd(>HGekkIkl8 zf1Z!`%qoF%0u;Ydc29Yxuc*OkUfQ32?_(YD-AA8(KEj5p;?7 z>hm%1T&-~;oI0P0lK0LA+AhB9KuYZw6f3?wv4`~6$niOgu{`!b%>;D4LDG`s(?Sgo zoUzRV!PTgt%dJI#`-;YP28vf0djHcOpHnqS_jAlj8EM|Fi7iXRI`arM!qY@&u;&sL zHF8D^uNBcnk(CEZ3~a$`5G%lL(-*H(Q?v-= zc3*FpR*tu#_KSKK{9kZNK6Nw?YW#+YOi^@Dm%3gxX6>8_)mJ;QX_yNR-22B~K(3`D zXvG#DCXy%}3`nUMA7J$-K8}lr4f|+mg|v2h@W@d~F~{Upngoe{ zk)Pk4_!tS*K_)E?I(7RAEPL_;T<<KQc~8t@HPF4&neVeabFejnW*i37EG5uXiz{=(3MYz)heA1{?Wm4g+k00m02E&cm z2eRDGzOhhEEiSWVq|IWfOiby$1@H>+81oSLE2=uJs)osP6_sHF=w9Bfo!c zlCslJNH0_y9d@0|_qV``u!Kq(a*9i)Nt*_Z4C|VDE!uzH$e;25XP#+-uFdb{*R@`2 z{G58D4`uUjfIQin#WeQVXHV3>5*1!=&3T|jxS#XAK(U))n(q9lYe_{2EMUB*h@YJ8 zNGV$O_Uj+`ur%HM5q93a-Ee<-qaZWp(cO{9=k#UIVY@eSR$Xg{_a4LVrc<`sVEtlx z3M+pt84^&H=)_47+YLJP`Y|rcJ0WiafHf0QAGuBngGatlHL9!zJ#zM~lG{;hC)}+l zF?;7*L#;NFcoA&7@AQLMGmFZ{2Bpc_m%f=GUqUs5x0qHN0oG}`?1sc?g{Fa^w49y7 z6v6d5YgYK($hF?g?aa=_C>Lm&N|A=r+u0`lpcBb2ysj_sG<7<0shaP8SF`a zQK2%My1HCcQlNLO+q^DVczXN6|1@zqetdAE0LBxBu1L^)84AAi8_1`E#r z;QB;U)(MMl;B@I~CPIsjj_*FRhGqR!9KdD#O0fy*2p4$Qg~eY&d5;|b?D!*b?qGK4 z5G!r2x)JjnQ7CN_7h4Ny1j5(4dC8bSjDK|8^`A^X+pg=gH-*l^0MQ<@ON?YIobtvyiTLPAKO|Tn`g$!j^gInZIf77OMqUz z@5|au(0*z9QA8v=RD=bgfQV(&k4qPX-_3iH(ODsyP3vIKj(S6api_H2bIZ1-IS-6{ zx$?yx?~XKDwD%=DG+#;Y(HonjD=>Q9r(V1ql%4;QuKN;&epLXQ`sU1D1`Vq&jnbm-$Vw=?jHpNL_%?!$ggi1?$+6Rpz!Q%f*Vs{_@YwU|O^!6Bb>(F21vU-F3PzcYEC zv@HyT*Z-M03T7tE^7eS`Ja^&ge*D&ZIpzpC|IR~#ma<4`HBExnZqRpAtn1eCJ&-+= z5p1~egTCg&-$^YKU3WoNZkq0&%+`$oJcvUPMN`EG%xY}t_v&KpcRn9xysO3o6*Ay& z^p;K1zhY;7nFWo>+=nJU%h)m!2JY5f`Ei1~dXbSnkMDDWH7Ok|U)2}xAsW$r+bZE~ z2Q~Tdk6H?j@}Q0=3+m8Yz9~7qAIaew%G{dh8=Yq_#9!X&PV7+r0f(RZIz;qlKVfGM zu>cKy1cz;<1IJJ7L0rv;_)#eK>Ld-B$4a3$pH46(-adldbhL(EVKipdG{4qfD8C3z zl2WSp*vGNiMundlZ%_>Syhd15hC#vW+1ndM5wXyMecb#>P<^6FSF5zEwqTI}20Mv= zBZ-El(GK5LdsTI`-ZzV`5dJ3Ws^h4w)RoDVu^I?};Ne5vE@N1X!b9sG+DfJ;{qOwc z0SISk3qk#*u{pQblEjeZeECr<-ShL?4(oU@brmfwtZaEcq6m91B*x!VfV~+gQ4+dV zLA_?Uj4vzI*n(5-BpP{@!Y(~IJj+wG8mEL0(Ol1VsPZuw3A>dn`epDfw1Bedwh-@2 zR~;|-ok7T5pMNYuUp&isJxpG65(ozGgp@zx0O$qFS6sU&A&T{HXX**gd=9@b_3#tD zv2YA3wqa8CavnE-q%E+aWRc1RNwS%YGk<%en6P-m^zjv#I>&~o@Q`Ryk$?-tBKOcz zS|=QjcuO4dC%|S2j*sYDc^V1G2f2* zj_h8|pE9M>*&BMJpQ7@#n;NobI_Dy=A?ecPQog)5Y%dgCbYv4Ws++7lN>2OUNdH47 zDee~xq)~WFNa^1j6$QU~334F1%TIM7du9n-=xVvF`zaD@0`DMIpCF$#ZqtHXPG0My z*WpK+a&I{Y_OzTT`i!QiK_nh;-`N^36%JaNk)*`)pJTNjfFCCHgChOgJJ z3k2Sa&11RvqKGCkB}a?0TL3uKVaS#oI^-#!PKlrklu{jziGbo8vw{pcF@o+79gq}1 zWJV|m18!fjL*Fy9qkyiT3&iH(O@b@!BE`dj7KQl6{QpP(K>&DQcO}o(-hqmc-*1k5 zZ_xh7gWf^UNXNpH0C9T8cDECP<7La=OX9106IVlljY!|-i*x?9Ub=KY1^52Oe`~0^ zdw^aKIRWYktqVoJbDC@3c5vH?eD@as-Ug2Hh0)!xq=F-3*pvC)nMuyn?tU__b{al$<`Pw)(++$S z`t!4irRmgx2QknPZLe&f{(cXUh8%T6w{J7n%JY92PBbL<4t5QD%^(eL3>P-?xK^_I z)$fDJv%_0^<25XQG=$WYTA|)BE*)I32uej^ANSp}zP6v6pQdv4uhx1Baqr}*Ij?yB zn*7H3qBJHtE{TgcNRn;*=2DKh;9 z8<&APShbGZRix>qH1E{MLInyIt2u1b>p@mBeshfqfk_i3R}y=CFm5+o<pLaC$nf8@FGe>=7edW_N^^ec*8TTgc= zIF?7lE82gbH>|n$gt8fHC)_9zYkfN`7bb@u=Q_hGpZ4*r&)n1MBNEj0qrjOd=Mf1U zu}6ET4tM4-ia_NKC+*7TTUQbzg!W&ostzCL+67AJ*PU3t`xd1ZNE-(Se_ml zY@5K-jg)3b`Mq&@s44o{;`{YmuxHF}*%(zM$qI(pBPQ>A4slB??L7C-QC0%n><|~a z?r^^)xrs39^<1;(y$+%$S>3hRNUOT)7D_qQVb{UBu-7mOvBDW#uTld& zld?yq0Rntr6@pT|eWSjfQ9ff<#-<5)2&61mq4J2fIB!)$(Z9ei}8SI?fD|Hfh62bh}TO!#7A#ucNMYD~+zQS!{&w9d(!8^r*<6wPJ(+;E}K zPSb3N())WN_jW2;b!M4X80Xx%n9d)QSHB0! zzZ6WPNX9LqkQb0P@!bprY1Vo^Z9f8vh~8b3A_ID@|8T4SpR!|na99{CCN_)h2Ul&i;tKuYzt8)=1K+o@mL`@Odn`}=-lzbM}oX0V#o zI|@gZJSb;}XkoDY!9$yhGnd&`SNisIQY{aOv(sOb5d$!qOEGz4x%VFUIJxjf7X zycXdDpiLjGdg5%g&yNMgsP|`|ych1h&kObi%#}H zhaz@Whrj0TclvWpd`k_eqJ9XWA&&=wX6#7YuKK4`5?!~uMm@Ll)H*3OVCH&>djQ4e zOnM$Sz|VKE+~}zym9}IVo0rfvjy_43J_`pE7`M1q&ME0KW`HVE$(4O@M=xN zni{%Xkqjs8hwqMv5l9MjT@4_dZQ~%be|Z+qmU05InWeNE1YHFG9_V(E$cl4KgxU+^;RFdo;5nfZ;B;R}4!;Ih^sFwig7~Ypx zwk=&U66FMu0it4&n1r8zj;>&pombf&GvegX;Wu&hRkz`9G;J&9PM0p?Kr#`Q zMX`Edq4Xi*d!}DRz=`qxeVXg!%jc!bv$(<}_9m-2ol|YDp6x40aAiZ^8K2}l>D_xJ z2N=q?IBZJN0{cvK>*_|ZB|Dvoli%Nope@i#a+}PE z@SkVCT`Qv7;r_P;`7dCMO5e7;CH__U+cl`L{y@}SQEpKD*Fu-6G*Y_vK1itP5swr$ z+&=nr0l+eF(DrO;(w)m8%oQZW8XJ^;>pyP?Cyk+9@_V*9>Fg4={yXXK)sWXU&Fy4b zT4cCs3a~x4aqGMnC5YkYD1mH=0{tj(T0Be}EduxIgbz(5zyWuxQW|yL^3$h9VEnm! zKe8k{%^!AM?}^PDQ{aj67~Lyk{NlU)>*Mbu>e-DRA-ooFgPl!QiU_V8ZGi(3EiNS5 z^_78G(%ow8Ea*MdXQU#Rwp<#({8RPi{-xU4+n-&*v0V3AiXvbXug%(&`}SBod6sm6 zMy%x+lq=>MshjX^VZb34@OWCJ(7`||>6Pu751rH2TQwnjoBCiR99$W;)L;_{mMG^tx{iO8FhEE9(+MeN7JF2nk-&_R~^QfW}J98S2n>!#f7vzo2@E_nxEtY`)!cX**DI^+OHlEM~kBvh+FD;>a zRQbsv01KU~3#BP*c>96Y2AiO1k0Ywhc6KmiN1ZfzDgl%81l*1t8_TrClHD&tt-#Ak zrDxrKSNll`csbAXf1qIq40tjfZVTgPJ}Mc(ZTci-G-T`@9k*liw!6~mX1J}Z$Pq|i zK2@VfAA5)fQe1X3GmQH5pf-W*4l;UPD=Sevpe)o*3-@4~Wh-ic%$>^Pfhu|YXpJE96e$a zAK^&}7iRc*saMk@kO9i`a9iubXlMn9mMy8{ zpYB5STn-}uO8)0wfO;|QD8mCjU^sCd2uI#k;m;cBB6>XXQZM}!t6)`aj^LwXK}>6r zvsO3^$-qnI!0vdGQh8^%f>16Un41Qj@*W$npFWobcI3(WvkQbePi!x29iCzx*QF?9 z&gU0k;WZ0nv{WADB`i1RdV|>ciQEL-QmUWIiK6T1YYyo)#Y&ovu@VkXgIF@8Hry^h zEaemRPQkA~0(Z;hwy-SxuTCH}-J=k<-Tu!kQ`a1K<=}YirQItqvi$5%CY{Oc{&6NR zd%;nxB6pAmxq3NtVl)l){^Ww23NHVJcQBMDJ3Jyp&W^V70p2|hYuM{A*&^(y+xvsd z7}~SNnP>o?qzv&v>SsMGke7M{<5P2cKRT(FrshCX=3-_%dSm=1saVNfGTds;B_`}$ za|U$XX`P)~Pt6nYP&iG&>_?(fUCxqw=EsYMT%8;E1!a11&?z2MBX*RF{!m(Ii0;ho zPd@CZrNOl~*dy@*tiFn_ZV}E_h}AksqqtDRpoH67Lgjb*OM3w@V;|<~5OH3#`kq0d zOa%?+j3l#BOZe)gs9rqGooRA^TCdo&-FHCkk0XYp-^l+i=T~Qp#iBm`VKK&pMM0Pxx+xfgsvn&k%VNN%9>7 z+paRrBKr^Ot0|sbKwW!@gx(|wiT!m1>s-0XX2}n7#^#p(q502KOz4=Y^B1Bmr~kjX z!-onLvpUf$#LX5#H~tSWKg9?mF;OqIp&eZy0S6lY9|PX@@8+W5VV+%tF8}$cywS#& zG1}yuEu_G3_#YrcN+TA-^!?dMgA7d=2z&jz04aOgN95BoV+#NMVyq=24sW@4HEeI> zacD=E;L)Lq^9r$K08s+BBE^HGj@9+C<6qiU=$vi@5YSSRM-tS zp|U?3C7uQ74hSwr&mx2Dt*wLB6&)V-k~ccBqnd0?#=nrd zj#{Yh7zh|GofNE7>X1v`@>`l?y9p3VnPQpC^BEy}B?sZ`{9Lm)RMf`f)0Cuc@q-Mj3=pKri1rfTBcSW}1FyH8iaP1%VyTe5&FGm#>Y?Kvxnzd<>Fl1OD{ zJhVXR@%rS2&rA4)VKqZh^Qp2g5ZUV#U7^W%4wyRVt~ir344)-FRz$;TKHv<2cz1n%q?VT7eU#E_`y>+?$`#rdtfzdisPn=7fif5jkH`#f6`6# zAbbN(t>=9h493|}MUm{~GCJRDmu0jrg5zwrVe0&@_&I2N9HULp{|n;LO(LlV_!|=t zGYyQRc)*5$g};8>yfg#DO~q0nkWW}?SIF*;@&JgZ2|wh>pQ&9iSn~q&SCUCZcFp1s zMPIMXvE75IYqdVX%yl#L*8kGM{g1|`w+w6P?Pq^e2W0PwLMglz23$pMG5|T$sP9Jl zd{eks#jgdwfQW{{wPfFDyvc7AtX<8;$>YT6y6Q%YncO1KNd#V4rp)%0=pkg#0K?l4 zW;VHxytF5BtdPJ(y~r*_gAWk0Am*oY?Xo82zwZ7g$R7e%^CI>jwJiWXD4fPC!WZc0 zDzH8j=TeEMOZTKf8USQLvmCPtKeQ5WupnTeK25W?v9XR@SP5pP3&2QMRNLA&%Nl!U z?##ADax8LG-457Z^vMMjF?o@|ooWTD?Xn6d5!V7B^R1iYSQ-EVwj4b>%H}y3FEZcD z84|`Z5>$W4wn+|n$WP<{%^@}~QbB441VYw!+Hv!EN6t_uUf1c)0TU^ZBZ>Yn2@)=X zH2@D#Hhr>VVw%?yGs1531b6B(C13e{&O!)&m%jYmnbMut0AO?dIrkK20;5m#{OU#Q7v5cSBmktw_5e-O#0mvM z7^7{{DqMM4T9)XWDL3`4N>s7ffqmw~y!j8Qj%Ed*s{_p@iy1cRYEK{|;BXhdzKqqk zzx~Tva4xw z6=?$rKvLxOm0^hyXbxySRZgQBU3-+EOovE4(NieL4QZ`r8 zqVAn?kU0Hv+s$X>D-fniK`ZXh8ub|6+7za~iHcskT|=)@P=kN-4>K2fO;9;_sSUrI z@MW>V{+$U8U?oFJoKe>;!uNX{!7lKwFOt*7eyYPC!>5JCfF<$oa!i{3toiHfwD8U- zX^`m^k8MTuYVcm{yTAK4?F)6fydVY&OVf0B!b~_ZJ{f;U4qOhHN(1}$Qqn0-7MN$2 zHYwz-HcFG<;#qx1UK(Fs1dOpw&-7`T9L^Io0rsvL@W`zxog_dtd78x{AMpU^zH=eU zX#jl4qG`0qjbiVptSk9GzsRDC@jpm@PWJx=$S*W9bW#$;klH&Vhk8Q2S%GZ=zv}hO zw;;9Ugz-x-JHOuk<#@)BnG?|UQb=PEruEM);FiNfB52Lgb+>`V{F>1( zp;lBgi_SMV5ds6E--B0xdp*mW>pRr=_##Iij@4LNXTBH{=0pum5Z1||X+m`hZ`nKh z3@vTh(^7-0jqK*Ctt&S2IXl1de%Q!rCBubKREPUtGulmTHsL19lq_8i-s7#YKmX(9dUp?F6pUGP^&RYUPZa9TI3skoUk_G&q z2>}R=tWw^&GI3F|Bme4+1PNjg_}nmsPIi7D1r%7MUg-T%?j}$XzWCZYnlU4g-R3*M zy8zk`88zLW`5x|B{5FmOPrp>*E4pAiF;JHb`fvxdWGU;!0mE6@AZzHWjD`b^He)I4 zBgm~lGYwx>wMS%iToy2l!GLI;JX-Hb=Q@-Dl-TCIc*bsX09L_bf#&>Unfxzj)cFIn z1$LmKoOWisi!jjTwrDW4~)eQeF%)t6XyTR zLUn$Nav)+y9sNmBDVpE9Q8ebJInEdXOlAw`E;s@3%xXAL$fvn<7A0Z@n6%^5TW8B{ zsn5te;Ht8xX5xk0Wn+< zI__)S36gXto2}js-f-GTsj#ap*>BmY3f}PEKvw#O_&#y(AsQ-fVfsN^S$Y3U;@~AY zL`=pzpV>Cx~8WPM(Piy-wKjHNh}C;b9mO@mNHu- z=PuuEu<0$dailHgP2LSmSpVd>x)gFX?%Ysz4U!?RozCcxt?YS@X)%v(qM^(U zpS?kXo)=)7w)Z0FR-gEn$BA;t$GY|9_#J=Uj;F;<4ZVcsT+`Ut@>9+KPDLx$edc@vJD3}DJf{S zd6+}kb;ezzv-@yKMIB@xqmhV|f{HqmeQHE^0a_DhtP>B-5#kW_5qFUZDYHEdLx8lT z4jW>mQg8V}~(M4LP91nTVwUS5=?Qm08xbiK-Ba(TsAKNJ#% zH~Q!nVkD^8w_OKS4THUX3<_oo?%A-F?HVYMp!TEx+0Q0~SQd}Qwlr*I6Axco%`(ny zc9L>{-Q|WURvCn@KcwPy1&{k$GY&71V-&Vi!EcDF9$kB&FQBt6J0WlxwDpLpB}bOC zW*XHSC;E~@^yOQBVlg$rjIt`mgbg7pi%o}emtl$&8Wrwb2lk+>YLdRdZ+v*y33m*s zQd~~-?2Yxqg4;yH9CNv4iW%QVT5lztFh{BE*XZC2Ux)wUOFUe3uax2K>&~5Y0HT0Q z9WL@|kdm;j6k1~PZpg~St121=?bC@2C`g2w4Y1l%Nc@cduC%pRoc#cYfjA>Z*>O{= zH#2kD_3LGgQ_VzRpEQwUYnqEws9#=ANsWmuEfYk6_gPmoifJ7ZeUc30xm|?sfvW2L z9Alaxj(#P|_sDA=uyv)Ds52>rDbSZgyIm7TQeaPh$rMUWZ~4;df49NLmtcDrXT?HVfvCI)(OgdUrvw;C@R3E}Z&4?(@41_~3n zn{WLww{lh~FUETtK449JeMdz1-z!nQzk{@2u3)iq_InbYkO4YEV9$<1WONo#j$Lyp zO2Dvv0)w#I*0b!HPK3|Aumf!!3Gwzq+3u~ado-ii>j{M46vww0kJ~aT`YQOVUxT)r z!?hXZ*60eUi9w||W?B%Ol7`9ZaT~+;yPlxc=96;E^xFDp{VH;_fWY~dYf+?<+O*P^ zum})`8d;MA?Lpq>Q&9+|@j=GsiRYFq=;=4VWb3(2b(Hsc*~HZG)>)O8+%S(uq4Q3V z<-^3U^}L{wq)e8)UwTLjNly+s$Cl`lvqdY3^JzP67=CZ!fKCAZU-&y+EL zMH=Op>MO&CTMGRXegK}|KP3?VdjBMQZf`+|U_~p;X2%30z$TeIDGLzGhc%#4@yT;hwJ`=k_LmWlm0mJs6G@%EF~(*7UQyT$yLXx zQO_xf9^<|iJip`AE&K}36WA;fU~?yur^XyIdd4OVjt6% zaAKqn>^D%YFK(ge%rPjOBi@ZpQJ5Q?>lHB0@b3yMgPtgeYjd-pdV%407&Wm+H*R6_3ArPpqe<) z?cw7gnb?R{p>iB~`HljtKABeu50bC3$79>5wom100z4kOj%|2oHx> zppX{^qF&$tt#?1vDyrbe%i`jrFbv)Fy#f0~bY03QRKpAJY4*#YzVW07XWA1{`k?pd z`IW=lPGbVG{>MG`H>P1iA{ZnC|A;?o@DTd=6rfwAO$gH_qmadGL%(TEdkx4;Qwf8^8e%>L{Nli z73cR7Ia*~pt9KT~JPJ)X9s`{bIs<5lb(LK2f6ALbjM)`;zay)~-y2*$Xc>ClwL`D4 z^8d7O?}1FNe*nPWkV{OWD@Ah8A%|HJk!xFq5i!>&ZF6UGOdP78T+%6F;g}KT5*C{> z7V1>Qa+_8RAl%qW`@5#Utd*OWN}& zmCXyt*gv)I`7&5bL6=n-V;$P4ojVeL-D#iBq8yAr!j?r2IHB2YlKS$!j1Y=uch%|( zLvRhB%C-yBqmXimy!%;e3NuFIxbS~u%#_82#`w7Im7^wVmUD zn55Ph>*8qe1)tQ$R^r}1$I07G3LZiy6QPZz?ZwF6vNd-}L{UOHW&TfgQE0o{g$aEe zO|)XTO-h8G+@x5wKR5!te$>f7&K@4m^JI7wdt zAHr3uOY@yeg>WB;Pa>{NRZ#lU$>@p6w_y={``L^*hqS}v$0tAJkv zZ5eYoa0KSkUF4noW3e+iR_iDJK7Pe~%=nZ;3)u>`ze(5`ap^+CeWxT@>j_po`IFCxP#8$N zL`p-c6dEoDsnA_xb7uDGHM1s!y}EC)Es?C?laoKc5EG+R6zcM@lk#6!%{KG_8XDP@4exSG4+lL?A;9b0i^mkqjNqxU#bitFGgQyHrdCLV98^-D#VkCGr zW~XAyHJsTNZLsnMzi-yRr=Z4^Z|me9pW1(;fKn}imOv zBXfGc*>R?p#zT=&q55DnBNEFda&#GI|e%PG&cAl(67J7{F zxJn?NbK6gp9?w5@&XuG&iqSZ~C&Ey_A|Zl zs246q>TU_Lv-IPkC=|k_qrX0xllDy21sSKqtjnZn+N}7udRDb8x6ez<4qE{*rPkoD zkIRh&Jv_=~c5TT0@i6mhMVmgJV9dE!T`2n*&@4wr(T-~|5rNyV#z>HKWCJ_ei7?g+ zD5yvKb0pt1GiFRjM0K71BQe$Tu=E{ookYg7up5q`8BVPsb``sg!teL74PJPM0aC{+PZyns zhl|jwN>reds5sdgCI^VPZc|{frIgf6d$$DoH0h$3%`CNURkhj2Bjl+Wr3wM{x@g-g zpVs3_t|u>O4@u0QL@~A1N6iydZW-PsKJqN;Lc-To{dVR4TJY<;Jy(Y4y2lpfYq9IEKWTX0twW463E!5o_GB<9-jH(xKJ$-ZX z+5T{PMOJgrOP2>i{P+*n59R&)fUqSOIGUM6SbV1tDi6%Atq-cDg@wDbWX%BFld%gY z&(Yfl0%Tav7VzRBKkTbbWPm~4vqG9!kuRw(eaY(B3>9{bXhFte?PX#(w*3H_;DK%YtJ6ZP9T5w@tdLU zFF*hri@OJ++#}#j=<#}Tk(}|GrDbP>sKk5!*H(TAw6*>gVm&KzVj(tXSV*X^CU+uI z-c;glEth~eRS~qk7?t^joc+2>QiLyI$N~YE6<6Fa-{~Yn*%{zTlHS1A&aeMHWuWt! zG4+V40|iQ?vJr4cMora5)MB@=V-U!z9|#v)o_Fu>uqS~M;?K6`xkVFsbIRNVD)wU1k$%Y;V^Wc2S`NyGdG4lDKVXz`Nxukl+ zBnVoOKVII?vA4Mi3f&BPJ>izs`dl+TQ?jj75t0wP4AFB**QZPcacytMnOmVKa1iuT z{pUknGu}rP>rRg_VgI(}GzdFoB8UR#vzqGv+H+|u%^qi6YF;@$kKXX_LU$EW;}kgA zk{YYih-e5p3b?qBRUQ7NcitD2V(R4bWndEg3et9&<9TnsSYd0O?z~*SX+QY?wcjm9 zi5n?sF{X{?s)`3;I<@$bLf{ae##+TpVaDAMZp3q9V_W! zkiMQV506D#PMrF7AfYld_$2!RuiKSv0R;gWkbm9Rk8rMTo{oR=zQ9@TpiLvieBD-D vye=C5QZpDSy}=zqP&Aks2*O(b&HEuYJN!RdMBl*?&;TKw-JO~pLNEOvntd^V literal 0 HcmV?d00001 diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs index 9c806370b..746fecafc 100644 --- a/DesktopEdge/MainWindow.xaml.cs +++ b/DesktopEdge/MainWindow.xaml.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections.Generic; using System.Windows; using System.Windows.Input; @@ -40,723 +40,734 @@ limitations under the License. using NLog.Targets; using Microsoft.Win32; -using System.Windows.Interop; -using Windows.UI.Notifications; -using Windows.Data.Xml.Dom; using Ziti.Desktop.Edge.Models; -using System.Reflection; -using System.Windows.Threading; namespace ZitiDesktopEdge { - public partial class MainWindow : Window { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public string RECOVER = "RECOVER"; - public System.Windows.Forms.NotifyIcon notifyIcon; - public string Position = "Bottom"; - private DateTime _startDate; - private System.Windows.Forms.Timer _tunnelUptimeTimer; - private DataClient serviceClient = null; - MonitorClient monitorClient = null; - private bool _isAttached = true; - private bool _isServiceInError = false; - private int _right = 75; - private int _left = 75; - private int _top = 30; - private int defaultHeight = 540; - public int NotificationsShownCount = 0; - private double _maxHeight = 800d; - public string CurrentIcon = "white"; - private string[] suffixes = { "Bps", "kBps", "mBps", "gBps", "tBps", "pBps" }; - private string _blurbUrl = ""; - - private DateTime NextNotificationTime; - private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); - - static System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); - - public static string ThisAssemblyName; - public static string ExecutionDirectory; - public static string ExpectedLogPathRoot; - public static string ExpectedLogPathUI; - public static string ExpectedLogPathServices; - - private static ZDEWViewState state; - static MainWindow() { - asm = System.Reflection.Assembly.GetExecutingAssembly(); - ThisAssemblyName = asm.GetName().Name; - state = (ZDEWViewState)Application.Current.Properties["ZDEWViewState"]; + public partial class MainWindow : Window { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public string RECOVER = "RECOVER"; + public System.Windows.Forms.NotifyIcon notifyIcon; + public string Position = "Bottom"; + private DateTime _startDate; + private System.Windows.Forms.Timer _tunnelUptimeTimer; + private DataClient serviceClient = null; + MonitorClient monitorClient = null; + private bool _isAttached = true; + private bool _isServiceInError = false; + private int _right = 75; + private int _left = 75; + private int _top = 30; + private int defaultHeight = 540; + public int NotificationsShownCount = 0; + private double _maxHeight = 800d; + public string CurrentIcon = "white"; + private string[] suffixes = { "Bps", "kBps", "mBps", "gBps", "tBps", "pBps" }; + private string _blurbUrl = ""; + + private DateTime NextNotificationTime; + private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); + + static System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); + + public static string ThisAssemblyName; + public static string ExecutionDirectory; + public static string ExpectedLogPathRoot; + public static string ExpectedLogPathUI; + public static string ExpectedLogPathServices; + + private static ZDEWViewState state; + static MainWindow() { + asm = System.Reflection.Assembly.GetExecutingAssembly(); + ThisAssemblyName = asm.GetName().Name; + state = (ZDEWViewState)Application.Current.Properties["ZDEWViewState"]; #if DEBUG - ExecutionDirectory = @"C:\Program Files (x86)\NetFoundry, Inc\Ziti Desktop Edge"; + ExecutionDirectory = @"C:\Program Files (x86)\NetFoundry, Inc\Ziti Desktop Edge"; #else ExecutionDirectory = Path.GetDirectoryName(asm.Location); #endif - ExpectedLogPathRoot = Path.Combine(ExecutionDirectory, "logs"); - ExpectedLogPathUI = Path.Combine(ExpectedLogPathRoot, "UI", $"{ThisAssemblyName}.log"); - ExpectedLogPathServices = Path.Combine(ExpectedLogPathRoot, "service", $"ziti-tunneler.log"); - } - - async private void IdentityMenu_OnMessage(string message) { - await ShowBlurbAsync(message, ""); - } - - private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { - LoadIdentities(true); - } - - private List identities { - get { - return (List)Application.Current.Properties["Identities"]; - } - } - - /// - /// The MFA Toggle was toggled - /// - /// True if the toggle was on - private async void MFAToggled(bool isOn) { - if (isOn) { - ShowLoad("Generating MFA", "MFA Setup Commencing, please wait"); - - await serviceClient.EnableMFA(this.IdentityMenu.Identity.Identifier); - } else { - this.ShowMFA(IdentityMenu.Identity, 3); - } - - HideLoad(); - } - - /// - /// When a Service Client is ready to setup the MFA Authorization - /// - /// The service client - /// The MFA Event - private void ServiceClient_OnMfaEvent(object sender, MfaEvent mfa) { - HideLoad(); - this.Dispatcher.Invoke(async () => { - if (mfa.Action == "enrollment_challenge") { - string url = HttpUtility.UrlDecode(mfa.ProvisioningUrl); - string secret = HttpUtility.ParseQueryString(url)["secret"]; - this.IdentityMenu.Identity.RecoveryCodes = mfa?.RecoveryCodes?.ToArray(); - SetupMFA(this.IdentityMenu.Identity, url, secret); - } else if (mfa.Action == "auth_challenge") { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsMFANeeded = true; - identities[i].IsTimingOut = false; - break; - } - } - } else if (mfa.Action == "enrollment_verification") { - if (mfa.Successful) { - var found = identities.Find(id => id.Identifier == mfa.Identifier); - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsMFANeeded = false; - identities[i].IsMFAEnabled = true; - identities[i].IsTimingOut = false; - identities[i].LastUpdatedTime = DateTime.Now; - for (int j = 0; j < identities[i].Services.Count; j++) { - identities[i].Services[j].TimeUpdated = DateTime.Now; - identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; - } - found = identities[i]; - found.IsMFAEnabled = true; - break; - } - } - if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; - ShowMFARecoveryCodes(found); - } else { - await ShowBlurbAsync("Provided code could not be verified", ""); - } - } else if (mfa.Action == "enrollment_remove") { - if (mfa.Successful) { - var found = identities.Find(id => id.Identifier == mfa.Identifier); - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsMFAEnabled = false; - identities[i].IsMFANeeded = false; - identities[i].LastUpdatedTime = DateTime.Now; - identities[i].IsTimingOut = false; - for (int j = 0; j < identities[i].Services.Count; j++) { - identities[i].Services[j].TimeUpdated = DateTime.Now; - identities[i].Services[j].TimeoutRemaining = 0; - } - found = identities[i]; - break; - } - } - if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; - await ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", ""); - } else { - await ShowBlurbAsync("MFA Removal Failed", ""); - } - } else if (mfa.Action == "mfa_auth_status") { - var found = identities.Find(id => id.Identifier == mfa.Identifier); - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsTimingOut = false; - identities[i].IsMFANeeded = !mfa.Successful; - identities[i].LastUpdatedTime = DateTime.Now; - for (int j = 0; j < identities[i].Services.Count; j++) { - identities[i].Services[j].TimeUpdated = DateTime.Now; - identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; - } - found = identities[i]; - break; - } - } - if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; - // ShowBlurb("mfa authenticated: " + mfa.Successful, ""); - } else { - await ShowBlurbAsync("Unexpected error when processing MFA", ""); - logger.Error("unexpected action: " + mfa.Action); - } - - LoadIdentities(true); - }); - } - - /// - /// Show the MFA Setup Modal - /// - /// The Ziti Identity to Setup - public void SetupMFA(ZitiIdentity identity, string url, string secret) { - MFASetup.Opacity = 0; - MFASetup.Visibility = Visibility.Visible; - MFASetup.Margin = new Thickness(0, 0, 0, 0); - MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); - MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - MFASetup.ShowSetup(identity, url, secret); - ShowModal(); - } - - /// - /// Show the MFA Authentication Screen when it is time to authenticate - /// - /// The Ziti Identity to Authenticate - public void MFAAuthenticate(ZitiIdentity identity) { - this.ShowMFA(identity, 1); - } - - /// - /// Show MFA for the identity and set the type of screen to show - /// - /// The Identity that is currently active - /// The type of screen to show - 1 Setup, 2 Authenticate, 3 Remove MFA, 4 Regenerate Codes - private void ShowMFA(ZitiIdentity identity, int type) { - MFASetup.Opacity = 0; - MFASetup.Visibility = Visibility.Visible; - MFASetup.Margin = new Thickness(0, 0, 0, 0); - - DoubleAnimation animatin = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); - animatin.Completed += Animatin_Completed; - MFASetup.BeginAnimation(Grid.OpacityProperty, animatin); - MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - - MFASetup.ShowMFA(identity, type); - - ShowModal(); - } - - private void Animatin_Completed(object sender, EventArgs e) { - MFASetup.AuthCode.Focusable = true; - MFASetup.AuthCode.Focus(); - } - - /// - /// Show the MFA Recovery Codes - /// - /// The Ziti Identity to Authenticate - async public void ShowMFARecoveryCodes(ZitiIdentity identity) { - if (identity.IsMFAEnabled) { - if (identity.IsMFAEnabled && identity.RecoveryCodes != null) { - MFASetup.Opacity = 0; - MFASetup.Visibility = Visibility.Visible; - MFASetup.Margin = new Thickness(0, 0, 0, 0); - MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); - MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - - MFASetup.ShowRecovery(identity.RecoveryCodes, identity); - - ShowModal(); - } else { - this.ShowMFA(IdentityMenu.Identity, 2); - } - } else { - await ShowBlurbAsync("MFA is not setup on this Identity", ""); - } - } - - /// - /// Show the modal, aniimating opacity - /// - private void ShowModal() { - ModalBg.Visibility = Visibility.Visible; - ModalBg.Opacity = 0; - DoubleAnimation animation = new DoubleAnimation(.8, TimeSpan.FromSeconds(.3)); - ModalBg.BeginAnimation(Grid.OpacityProperty, animation); - } - - /// - /// Close the various MFA windows - /// - /// The close button - /// The event arguments - private void CloseComplete(object sender, EventArgs e) { - MFASetup.Visibility = Visibility.Collapsed; - } - - /// - /// Hide the modal animating the opacity - /// - private void HideModal() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - animation.Completed += ModalHideComplete; - ModalBg.BeginAnimation(Grid.OpacityProperty, animation); - } - - /// - /// When the animation completes, set the visibility to avoid UI object conflicts - /// - /// The animation - /// The event - private void ModalHideComplete(object sender, EventArgs e) { - ModalBg.Visibility = Visibility.Collapsed; - } - - /// - /// Close the MFA Screen with animation - /// - private void DoClose(bool isComplete) { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); - animation.Completed += CloseComplete; - MFASetup.BeginAnimation(Grid.OpacityProperty, animation); - MFASetup.BeginAnimation(Grid.MarginProperty, animateThick); - HideModal(); - if (isComplete) { - if (MFASetup.Type == 1) { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == MFASetup.Identity.Identifier) { - identities[i] = MFASetup.Identity; - identities[i].LastUpdatedTime = DateTime.Now; - } - } - } - } - if (IdentityMenu.IsVisible) { - if (isComplete) { - if (MFASetup.Type == 2) { - ShowRecovery(IdentityMenu.Identity); - } else if (MFASetup.Type == 3) { - } else if (MFASetup.Type == 4) { - ShowRecovery(IdentityMenu.Identity); - } - } - IdentityMenu.UpdateView(); - } - LoadIdentities(true); - } - - private void AddIdentity(ZitiIdentity id) { - semaphoreSlim.Wait(); - if (!identities.Any(i => id.Identifier == i.Identifier)) { - identities.Add(id); - } - semaphoreSlim.Release(); - } - - private System.Windows.Forms.ContextMenu contextMenu; - private System.Windows.Forms.MenuItem contextMenuItem; - private System.ComponentModel.IContainer components; - public MainWindow() { - InitializeComponent(); - NextNotificationTime = DateTime.Now; - SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; - string nlogFile = Path.Combine(ExecutionDirectory, ThisAssemblyName + "-log.config"); - - - ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompat_OnActivated; - - bool byFile = false; - if (File.Exists(nlogFile)) { - LogManager.Configuration = new XmlLoggingConfiguration(nlogFile); - byFile = true; - } else { - var config = new LoggingConfiguration(); - // Targets where to log to: File and Console - var logfile = new FileTarget("logfile") { - FileName = ExpectedLogPathUI, - ArchiveEvery = FileArchivePeriod.Day, - ArchiveNumbering = ArchiveNumberingMode.Rolling, - MaxArchiveFiles = 7, - AutoFlush = true, - Layout = "[${date:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", - }; - var logconsole = new ConsoleTarget("logconsole"); - - // Rules for mapping loggers to targets - config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); - config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); - - // Apply config - LogManager.Configuration = config; - } - logger.Info("============================== UI started =============================="); - logger.Info("logger initialized"); - logger.Info(" - version : {0}", asm.GetName().Version.ToString()); - logger.Info(" - using file: {0}", byFile); - logger.Info(" - file: {0}", nlogFile); - logger.Info("========================================================================"); - - App.Current.MainWindow.WindowState = WindowState.Normal; - App.Current.MainWindow.Deactivated += MainWindow_Deactivated; - App.Current.MainWindow.Activated += MainWindow_Activated; - App.Current.Exit += Current_Exit; - App.Current.SessionEnding += Current_SessionEnding; - - - this.components = new System.ComponentModel.Container(); - this.contextMenu = new System.Windows.Forms.ContextMenu(); - this.contextMenuItem = new System.Windows.Forms.MenuItem(); - this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.contextMenuItem }); - - this.contextMenuItem.Index = 0; - this.contextMenuItem.Text = "&Close UI"; - this.contextMenuItem.Click += new System.EventHandler(this.contextMenuItem_Click); - - - notifyIcon = new System.Windows.Forms.NotifyIcon(); - notifyIcon.Visible = true; - notifyIcon.Click += TargetNotifyIcon_Click; - notifyIcon.Visible = true; - notifyIcon.BalloonTipClosed += NotifyIcon_BalloonTipClosed; - notifyIcon.MouseClick += NotifyIcon_MouseClick; - notifyIcon.ContextMenu = this.contextMenu; - - IdentityMenu.OnDetach += OnDetach; - MainMenu.OnDetach += OnDetach; - - this.MainMenu.MainWindow = this; - this.IdentityMenu.MainWindow = this; - SetNotifyIcon("white"); - - this.PreviewKeyDown += KeyPressed; - MFASetup.OnLoad += MFASetup_OnLoad; - MFASetup.OnError += MFASetup_OnError; - IdentityMenu.OnMessage += IdentityMenu_OnMessage; - } - - async private void MFASetup_OnError(string message) { - await ShowBlurbAsync(message, "", "error"); - } - - private void ToastNotificationManagerCompat_OnActivated(ToastNotificationActivatedEventArgsCompat e) { - this.Dispatcher.Invoke(() => { - if (e.Argument != null && e.Argument.Length > 0) { - string[] items = e.Argument.Split(';'); - if (items.Length > 0) { - string[] values = items[0].Split('='); - if (values.Length == 2) { - string identifier = values[1]; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == identifier) { - ShowMFA(identities[i], 1); - break; - } - } - } - } - } - }); - } - - private void KeyPressed(object sender, KeyEventArgs e) { - if (e.Key == Key.Escape) { - if (IdentityMenu.Visibility == Visibility.Visible) IdentityMenu.Visibility = Visibility.Collapsed; - else if (MainMenu.Visibility == Visibility.Visible) MainMenu.Visibility = Visibility.Collapsed; - } - } - - private void MFASetup_OnLoad(bool isComplete, string title, string message) { - if (isComplete) HideLoad(); - else ShowLoad(title, message); - } - - private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e) { - if (notifyIcon != null) { - notifyIcon.Visible = false; - notifyIcon.Icon.Dispose(); - notifyIcon.Dispose(); - notifyIcon = null; - } - Application.Current.Shutdown(); - } - - private void Current_Exit(object sender, ExitEventArgs e) { - if (notifyIcon != null) { - notifyIcon.Visible = false; - if (notifyIcon.Icon != null) { - notifyIcon.Icon.Dispose(); - } - notifyIcon.Dispose(); - notifyIcon = null; - } - } - - private void contextMenuItem_Click(object Sender, EventArgs e) { - Application.Current.Shutdown(); - } - - private void NotifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { - if (e.Button == System.Windows.Forms.MouseButtons.Left) { - System.Windows.Forms.MouseEventArgs mea = (System.Windows.Forms.MouseEventArgs)e; - this.Show(); - this.Activate(); - //Do the awesome left clickness - } else if (e.Button == System.Windows.Forms.MouseButtons.Right) { - //Do the wickedy right clickness - } else { - //Some other button from the enum :) - } - } - - private void NotifyIcon_BalloonTipClosed(object sender, EventArgs e) { - var thisIcon = (System.Windows.Forms.NotifyIcon)sender; - thisIcon.Visible = false; - thisIcon.Dispose(); - } - - private void Window_MouseDown(object sender, MouseButtonEventArgs e) { - OnDetach(e); - } - - private void OnDetach(MouseButtonEventArgs e) { - if (e.ChangedButton == MouseButton.Left) { - _isAttached = false; - IdentityMenu.Arrow.Visibility = Visibility.Collapsed; - Arrow.Visibility = Visibility.Collapsed; - MainMenu.Detach(); - this.DragMove(); - } - } - - private void MainWindow_Activated(object sender, EventArgs e) { - Placement(); - this.Show(); - this.Visibility = Visibility.Visible; - this.Opacity = 1; - } - - private void MainWindow_Deactivated(object sender, EventArgs e) { - if (this._isAttached) { - this.Visibility = Visibility.Hidden; - } - } - - private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { - if (notifyIcon != null) { - notifyIcon.Visible = false; - notifyIcon.Icon.Dispose(); - notifyIcon.Dispose(); - notifyIcon = null; - } - Application.Current.Shutdown(); - } - - private void SetCantDisplay(string title, string detailMessage, Visibility closeButtonVisibility) { - this.Dispatcher.Invoke(() => { - NoServiceView.Visibility = Visibility.Visible; - CloseErrorButton.IsEnabled = true; - CloseErrorButton.Visibility = closeButtonVisibility; - ErrorMsg.Content = title; - ErrorMsgDetail.Content = detailMessage; - SetNotifyIcon("red"); - _isServiceInError = true; - UpdateServiceView(); - }); - } - - private void TargetNotifyIcon_Click(object sender, EventArgs e) { - this.Show(); - this.Activate(); - Application.Current.MainWindow.Activate(); - } - - private void UpdateServiceView() { - if (_isServiceInError) { - AddIdAreaButton.Opacity = 0.1; - AddIdAreaButton.IsEnabled = false; - AddIdButton.Opacity = 0.1; - AddIdButton.IsEnabled = false; - ConnectButton.Opacity = 0.1; - StatArea.Opacity = 0.1; - } else { - AddIdAreaButton.Opacity = 1.0; - AddIdAreaButton.IsEnabled = true; - AddIdButton.Opacity = 1.0; - AddIdButton.IsEnabled = true; - StatArea.Opacity = 1.0; - ConnectButton.Opacity = 1.0; - } - TunnelConnected(!_isServiceInError); - } - - private void App_ReceiveString(string obj) { - Console.WriteLine(obj); - this.Show(); - this.Activate(); - } - - async private void MainWindow_Loaded(object sender, RoutedEventArgs e) { - - Window window = Window.GetWindow(App.Current.MainWindow); - ZitiDesktopEdge.App app = (ZitiDesktopEdge.App)App.Current; - app.ReceiveString += App_ReceiveString; - - // add a new service client - serviceClient = new DataClient("ui"); - serviceClient.OnClientConnected += ServiceClient_OnClientConnected; - serviceClient.OnClientDisconnected += ServiceClient_OnClientDisconnected; - serviceClient.OnIdentityEvent += ServiceClient_OnIdentityEvent; - serviceClient.OnMetricsEvent += ServiceClient_OnMetricsEvent; - serviceClient.OnServiceEvent += ServiceClient_OnServiceEvent; - serviceClient.OnTunnelStatusEvent += ServiceClient_OnTunnelStatusEvent; - serviceClient.OnMfaEvent += ServiceClient_OnMfaEvent; - serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; - serviceClient.OnBulkServiceEvent += ServiceClient_OnBulkServiceEvent; - serviceClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; - serviceClient.OnControllerEvent += ServiceClient_OnControllerEvent; - Application.Current.Properties.Add("ServiceClient", serviceClient); - - monitorClient = new MonitorClient("ui"); - monitorClient.OnClientConnected += MonitorClient_OnClientConnected; - monitorClient.OnNotificationEvent += MonitorClient_OnInstallationNotificationEvent; - monitorClient.OnServiceStatusEvent += MonitorClient_OnServiceStatusEvent; - monitorClient.OnShutdownEvent += MonitorClient_OnShutdownEvent; - monitorClient.OnCommunicationError += MonitorClient_OnCommunicationError; - monitorClient.OnReconnectFailure += MonitorClient_OnReconnectFailure; - Application.Current.Properties.Add("MonitorClient", monitorClient); - - Application.Current.Properties.Add("Identities", new List()); - MainMenu.OnAttachmentChange += AttachmentChanged; - MainMenu.OnLogLevelChanged += LogLevelChanged; - MainMenu.OnShowBlurb += MainMenu_OnShowBlurb; - IdentityMenu.OnError += IdentityMenu_OnError; - - try { - await serviceClient.ConnectAsync(); - await serviceClient.WaitForConnectionAsync(); - } catch /*ignored for now (Exception ex) */{ - ShowServiceNotStarted(); - serviceClient.Reconnect(); - } - - try { - await monitorClient.ConnectAsync(); - await monitorClient.WaitForConnectionAsync(); - } catch /*ignored for now (Exception ex) */{ - monitorClient.Reconnect(); - } - - IdentityMenu.OnForgot += IdentityForgotten; - Placement(); - } - - private void MonitorClient_OnCommunicationError(object sender, Exception e) { - string msg = "Communication Error with monitor?"; - ShowError(msg, e.Message); - } - - private void MainMenu_OnShowBlurb(string message) { - _ = ShowBlurbAsync(message, "", "info"); - } - - private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) { - var found = identities.Find(id => id.Identifier == e.Identifier); - if (found == null) { - logger.Warn($"{e.Action} service event for {e.Identifier} but the provided identity identifier was not found!"); - return; - } else { - if (e.RemovedServices != null) { - foreach (var removed in e.RemovedServices) { - removeService(found, removed); - } - } - if (e.AddedServices != null) { - foreach (var added in e.AddedServices) { - addService(found, added); - } - } - LoadIdentities(true); - this.Dispatcher.Invoke(() => { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - } - - private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { - var displayMFARequired = false; - var displayMFATimout = false; - foreach (var notification in e.Notification) { - var found = identities.Find(id => id.Identifier == notification.Identifier); - if (found == null) { - logger.Warn($"{e.Op} event for {notification.Identifier} but the provided identity identifier was not found!"); - continue; - } else { - found.TimeoutMessage = notification.Message; - found.MaxTimeout = notification.MfaMaximumTimeout; - found.MinTimeout = notification.MfaMinimumTimeout; - - if (notification.MfaMinimumTimeout == 0) { - // display mfa token icon - displayMFARequired = true; - } else { - displayMFATimout = true; - } - - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - } - } - - // we may need to display mfa icon, based on the timer in UI, remove found.MFAInfo.ShowMFA setting in this function. - // the below function can show mfa icon even after user authenticates successfully, in race conditions - if (displayMFARequired || displayMFATimout) { - this.Dispatcher.Invoke(() => { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - LoadIdentities(true); - } - - private void ServiceClient_OnControllerEvent(object sender, ControllerEvent e) { - logger.Debug($"==== ControllerEvent : action:{e.Action} identifier:{e.Identifier}"); - // commenting this block, because when it receives the disconnected events, identities are disabled and - // it is not allowing me to click/perform any operation on the identity - // the color of the title is also too dark, and it is not clearly visible, when the identity is disconnected - /* if (e.Action == "connected") { + ExpectedLogPathRoot = Path.Combine(ExecutionDirectory, "logs"); + ExpectedLogPathUI = Path.Combine(ExpectedLogPathRoot, "UI", $"{ThisAssemblyName}.log"); + ExpectedLogPathServices = Path.Combine(ExpectedLogPathRoot, "service", $"ziti-tunneler.log"); + } + + async private void IdentityMenu_OnMessage(string message) { + await ShowBlurbAsync(message, ""); + } + + private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { + LoadIdentities(true); + } + + private List identities { + get { + return (List)Application.Current.Properties["Identities"]; + } + } + + /// + /// The MFA Toggle was toggled + /// + /// True if the toggle was on + private async void MFAToggled(bool isOn) { + if (isOn) { + ShowLoad("Generating MFA", "MFA Setup Commencing, please wait"); + + await serviceClient.EnableMFA(this.IdentityMenu.Identity.Identifier); + } else { + this.ShowMFA(IdentityMenu.Identity, 3); + } + + HideLoad(); + } + + /// + /// When a Service Client is ready to setup the MFA Authorization + /// + /// The service client + /// The MFA Event + private void ServiceClient_OnMfaEvent(object sender, MfaEvent mfa) { + HideLoad(); + this.Dispatcher.Invoke(async () => { + if (mfa.Action == "enrollment_challenge") { + string url = HttpUtility.UrlDecode(mfa.ProvisioningUrl); + string secret = HttpUtility.ParseQueryString(url)["secret"]; + this.IdentityMenu.Identity.RecoveryCodes = mfa?.RecoveryCodes?.ToArray(); + SetupMFA(this.IdentityMenu.Identity, url, secret); + } else if (mfa.Action == "auth_challenge") { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsMFANeeded = true; + identities[i].IsTimingOut = false; + break; + } + } + } else if (mfa.Action == "enrollment_verification") { + if (mfa.Successful) { + var found = identities.Find(id => id.Identifier == mfa.Identifier); + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsMFANeeded = false; + identities[i].IsMFAEnabled = true; + identities[i].IsTimingOut = false; + identities[i].LastUpdatedTime = DateTime.Now; + for (int j = 0; j < identities[i].Services.Count; j++) { + identities[i].Services[j].TimeUpdated = DateTime.Now; + identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; + } + found = identities[i]; + found.IsMFAEnabled = true; + break; + } + } + if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; + ShowMFARecoveryCodes(found); + } else { + await ShowBlurbAsync("Provided code could not be verified", ""); + } + } else if (mfa.Action == "enrollment_remove") { + if (mfa.Successful) { + var found = identities.Find(id => id.Identifier == mfa.Identifier); + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsMFAEnabled = false; + identities[i].IsMFANeeded = false; + identities[i].LastUpdatedTime = DateTime.Now; + identities[i].IsTimingOut = false; + for (int j = 0; j < identities[i].Services.Count; j++) { + identities[i].Services[j].TimeUpdated = DateTime.Now; + identities[i].Services[j].TimeoutRemaining = 0; + } + found = identities[i]; + break; + } + } + if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; + await ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", ""); + } else { + await ShowBlurbAsync("MFA Removal Failed", ""); + } + } else if (mfa.Action == "mfa_auth_status") { + var found = identities.Find(id => id.Identifier == mfa.Identifier); + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsTimingOut = false; + identities[i].IsMFANeeded = !mfa.Successful; + identities[i].LastUpdatedTime = DateTime.Now; + for (int j = 0; j < identities[i].Services.Count; j++) { + identities[i].Services[j].TimeUpdated = DateTime.Now; + identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; + } + found = identities[i]; + break; + } + } + if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; + // ShowBlurb("mfa authenticated: " + mfa.Successful, ""); + } else { + await ShowBlurbAsync("Unexpected error when processing MFA", ""); + logger.Error("unexpected action: " + mfa.Action); + } + + LoadIdentities(true); + }); + } + + /// + /// Show the MFA Setup Modal + /// + /// The Ziti Identity to Setup + public void SetupMFA(ZitiIdentity identity, string url, string secret) { + MFASetup.Opacity = 0; + MFASetup.Visibility = Visibility.Visible; + MFASetup.Margin = new Thickness(0, 0, 0, 0); + MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + MFASetup.ShowSetup(identity, url, secret); + ShowModal(); + } + + /// + /// Show the MFA Authentication Screen when it is time to authenticate + /// + /// The Ziti Identity to Authenticate + public void MFAAuthenticate(ZitiIdentity identity) { + this.ShowMFA(identity, 1); + } + + /// + /// Show MFA for the identity and set the type of screen to show + /// + /// The Identity that is currently active + /// The type of screen to show - 1 Setup, 2 Authenticate, 3 Remove MFA, 4 Regenerate Codes + private void ShowMFA(ZitiIdentity identity, int type) { + MFASetup.Opacity = 0; + MFASetup.Visibility = Visibility.Visible; + MFASetup.Margin = new Thickness(0, 0, 0, 0); + + DoubleAnimation animatin = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); + animatin.Completed += Animatin_Completed; + MFASetup.BeginAnimation(Grid.OpacityProperty, animatin); + MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + + MFASetup.ShowMFA(identity, type); + + ShowModal(); + } + + private void Animatin_Completed(object sender, EventArgs e) { + MFASetup.AuthCode.Focusable = true; + MFASetup.AuthCode.Focus(); + } + + /// + /// Show the MFA Recovery Codes + /// + /// The Ziti Identity to Authenticate + async public void ShowMFARecoveryCodes(ZitiIdentity identity) { + if (identity.IsMFAEnabled) { + if (identity.IsMFAEnabled && identity.RecoveryCodes != null) { + MFASetup.Opacity = 0; + MFASetup.Visibility = Visibility.Visible; + MFASetup.Margin = new Thickness(0, 0, 0, 0); + MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + + MFASetup.ShowRecovery(identity.RecoveryCodes, identity); + + ShowModal(); + } else { + this.ShowMFA(IdentityMenu.Identity, 2); + } + } else { + await ShowBlurbAsync("MFA is not setup on this Identity", ""); + } + } + + /// + /// Show the modal, aniimating opacity + /// + private void ShowModal() { + ModalBg.Visibility = Visibility.Visible; + ModalBg.Opacity = 0; + DoubleAnimation animation = new DoubleAnimation(.8, TimeSpan.FromSeconds(.3)); + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// Close the various MFA windows + /// + /// The close button + /// The event arguments + private void CloseComplete(object sender, EventArgs e) { + MFASetup.Visibility = Visibility.Collapsed; + } + + /// + /// Hide the modal animating the opacity + /// + private void HideModal() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + animation.Completed += ModalHideComplete; + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// When the animation completes, set the visibility to avoid UI object conflicts + /// + /// The animation + /// The event + private void ModalHideComplete(object sender, EventArgs e) { + ModalBg.Visibility = Visibility.Collapsed; + } + + /// + /// Close the MFA Screen with animation + /// + private void DoClose(bool isComplete) { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + animation.Completed += CloseComplete; + MFASetup.BeginAnimation(Grid.OpacityProperty, animation); + MFASetup.BeginAnimation(Grid.MarginProperty, animateThick); + HideModal(); + if (isComplete) { + if (MFASetup.Type == 1) { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == MFASetup.Identity.Identifier) { + identities[i] = MFASetup.Identity; + identities[i].LastUpdatedTime = DateTime.Now; + } + } + } + } + if (IdentityMenu.IsVisible) { + if (isComplete) { + if (MFASetup.Type == 2) { + ShowRecovery(IdentityMenu.Identity); + } else if (MFASetup.Type == 3) { + } else if (MFASetup.Type == 4) { + ShowRecovery(IdentityMenu.Identity); + } + } + IdentityMenu.UpdateView(); + } + LoadIdentities(true); + } + + private void AddIdentity(ZitiIdentity id) { + semaphoreSlim.Wait(); + if (!identities.Any(i => id.Identifier == i.Identifier)) { + identities.Add(id); + } + semaphoreSlim.Release(); + } + + private System.Windows.Forms.ContextMenu contextMenu; + private System.Windows.Forms.MenuItem contextMenuItem; + private System.ComponentModel.IContainer components; + public MainWindow() { + InitializeComponent(); + NextNotificationTime = DateTime.Now; + SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; + string nlogFile = Path.Combine(ExecutionDirectory, ThisAssemblyName + "-log.config"); + + + ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompat_OnActivated; + + bool byFile = false; + if (File.Exists(nlogFile)) { + LogManager.Configuration = new XmlLoggingConfiguration(nlogFile); + byFile = true; + } else { + var config = new LoggingConfiguration(); + // Targets where to log to: File and Console + var logfile = new FileTarget("logfile") { + FileName = ExpectedLogPathUI, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveNumbering = ArchiveNumberingMode.Rolling, + MaxArchiveFiles = 7, + AutoFlush = true, + Layout = "[${date:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", + }; + var logconsole = new ConsoleTarget("logconsole"); + + // Rules for mapping loggers to targets + config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); + config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); + + // Apply config + LogManager.Configuration = config; + } + logger.Info("============================== UI started =============================="); + logger.Info("logger initialized"); + logger.Info(" - version : {0}", asm.GetName().Version.ToString()); + logger.Info(" - using file: {0}", byFile); + logger.Info(" - file: {0}", nlogFile); + logger.Info("========================================================================"); + + App.Current.MainWindow.WindowState = WindowState.Normal; + App.Current.MainWindow.Deactivated += MainWindow_Deactivated; + App.Current.MainWindow.Activated += MainWindow_Activated; + App.Current.Exit += Current_Exit; + App.Current.SessionEnding += Current_SessionEnding; + + + this.components = new System.ComponentModel.Container(); + this.contextMenu = new System.Windows.Forms.ContextMenu(); + this.contextMenuItem = new System.Windows.Forms.MenuItem(); + this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.contextMenuItem }); + + this.contextMenuItem.Index = 0; + this.contextMenuItem.Text = "&Close UI"; + this.contextMenuItem.Click += new System.EventHandler(this.contextMenuItem_Click); + + + notifyIcon = new System.Windows.Forms.NotifyIcon(); + notifyIcon.Visible = true; + notifyIcon.Click += TargetNotifyIcon_Click; + notifyIcon.Visible = true; + notifyIcon.BalloonTipClosed += NotifyIcon_BalloonTipClosed; + notifyIcon.MouseClick += NotifyIcon_MouseClick; + notifyIcon.ContextMenu = this.contextMenu; + + IdentityMenu.OnDetach += OnDetach; + MainMenu.OnDetach += OnDetach; + + this.MainMenu.MainWindow = this; + this.IdentityMenu.MainWindow = this; + SetNotifyIcon("white"); + + this.PreviewKeyDown += KeyPressed; + MFASetup.OnLoad += MFASetup_OnLoad; + MFASetup.OnError += MFASetup_OnError; + IdentityMenu.OnMessage += IdentityMenu_OnMessage; + } + + async private void MFASetup_OnError(string message) { + await ShowBlurbAsync(message, "", "error"); + } + + private static ToastButton feedbackToastButton = new ToastButton() + .SetContent("Click here to collect logs") + .AddArgument("action", "feedback"); + + private void ToastNotificationManagerCompat_OnActivated(ToastNotificationActivatedEventArgsCompat e) { + this.Dispatcher.Invoke(() => { + if (e.Argument != null && e.Argument.Length > 0) { + string[] items = e.Argument.Split(';'); + if (items.Length > 0) { + string[] values = items[0].Split('='); + if (values.Length == 2) { + string identifier = values[1]; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == identifier) { + ShowMFA(identities[i], 1); + break; + } + } + } + } + } + + ToastArguments args = ToastArguments.Parse(e.Argument); + string value = null; + if (args.TryGetValue("action", out value)) { + this.Dispatcher.Invoke(() => { + MainMenu.CollectFeedbackLogs(e, null); + }); + } + this.Show(); + this.Activate(); + }); + } + + private void KeyPressed(object sender, KeyEventArgs e) { + if (e.Key == Key.Escape) { + if (IdentityMenu.Visibility == Visibility.Visible) IdentityMenu.Visibility = Visibility.Collapsed; + else if (MainMenu.Visibility == Visibility.Visible) MainMenu.Visibility = Visibility.Collapsed; + } + } + + private void MFASetup_OnLoad(bool isComplete, string title, string message) { + if (isComplete) HideLoad(); + else ShowLoad(title, message); + } + + private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e) { + if (notifyIcon != null) { + notifyIcon.Visible = false; + notifyIcon.Icon.Dispose(); + notifyIcon.Dispose(); + notifyIcon = null; + } + Application.Current.Shutdown(); + } + + private void Current_Exit(object sender, ExitEventArgs e) { + if (notifyIcon != null) { + notifyIcon.Visible = false; + if (notifyIcon.Icon != null) { + notifyIcon.Icon.Dispose(); + } + notifyIcon.Dispose(); + notifyIcon = null; + } + } + + private void contextMenuItem_Click(object Sender, EventArgs e) { + Application.Current.Shutdown(); + } + + private void NotifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { + if (e.Button == System.Windows.Forms.MouseButtons.Left) { + System.Windows.Forms.MouseEventArgs mea = (System.Windows.Forms.MouseEventArgs)e; + this.Show(); + this.Activate(); + //Do the awesome left clickness + } else if (e.Button == System.Windows.Forms.MouseButtons.Right) { + //Do the wickedy right clickness + } else { + //Some other button from the enum :) + } + } + + private void NotifyIcon_BalloonTipClosed(object sender, EventArgs e) { + var thisIcon = (System.Windows.Forms.NotifyIcon)sender; + thisIcon.Visible = false; + thisIcon.Dispose(); + } + + private void Window_MouseDown(object sender, MouseButtonEventArgs e) { + OnDetach(e); + } + + private void OnDetach(MouseButtonEventArgs e) { + if (e.ChangedButton == MouseButton.Left) { + _isAttached = false; + IdentityMenu.Arrow.Visibility = Visibility.Collapsed; + Arrow.Visibility = Visibility.Collapsed; + MainMenu.Detach(); + this.DragMove(); + } + } + + private void MainWindow_Activated(object sender, EventArgs e) { + Placement(); + this.Show(); + this.Visibility = Visibility.Visible; + this.Opacity = 1; + } + + private void MainWindow_Deactivated(object sender, EventArgs e) { + if (this._isAttached) { + this.Visibility = Visibility.Hidden; + } + } + + private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + if (notifyIcon != null) { + notifyIcon.Visible = false; + notifyIcon.Icon.Dispose(); + notifyIcon.Dispose(); + notifyIcon = null; + } + Application.Current.Shutdown(); + } + + private void SetCantDisplay(string title, string detailMessage, Visibility closeButtonVisibility) { + this.Dispatcher.Invoke(() => { + NoServiceView.Visibility = Visibility.Visible; + CloseErrorButton.IsEnabled = true; + CloseErrorButton.Visibility = closeButtonVisibility; + ErrorMsg.Content = title; + ErrorMsgDetail.Content = detailMessage; + SetNotifyIcon("red"); + _isServiceInError = true; + UpdateServiceView(); + }); + } + + private void TargetNotifyIcon_Click(object sender, EventArgs e) { + this.Show(); + this.Activate(); + Application.Current.MainWindow.Activate(); + } + + private void UpdateServiceView() { + if (_isServiceInError) { + AddIdAreaButton.Opacity = 0.1; + AddIdAreaButton.IsEnabled = false; + AddIdButton.Opacity = 0.1; + AddIdButton.IsEnabled = false; + ConnectButton.Opacity = 0.1; + StatArea.Opacity = 0.1; + } else { + AddIdAreaButton.Opacity = 1.0; + AddIdAreaButton.IsEnabled = true; + AddIdButton.Opacity = 1.0; + AddIdButton.IsEnabled = true; + StatArea.Opacity = 1.0; + ConnectButton.Opacity = 1.0; + } + TunnelConnected(!_isServiceInError); + } + + private void App_ReceiveString(string obj) { + Console.WriteLine(obj); + this.Show(); + this.Activate(); + } + + async private void MainWindow_Loaded(object sender, RoutedEventArgs e) { + + Window window = Window.GetWindow(App.Current.MainWindow); + ZitiDesktopEdge.App app = (ZitiDesktopEdge.App)App.Current; + app.ReceiveString += App_ReceiveString; + + // add a new service client + serviceClient = new DataClient("ui"); + serviceClient.OnClientConnected += ServiceClient_OnClientConnected; + serviceClient.OnClientDisconnected += ServiceClient_OnClientDisconnected; + serviceClient.OnIdentityEvent += ServiceClient_OnIdentityEvent; + serviceClient.OnMetricsEvent += ServiceClient_OnMetricsEvent; + serviceClient.OnServiceEvent += ServiceClient_OnServiceEvent; + serviceClient.OnTunnelStatusEvent += ServiceClient_OnTunnelStatusEvent; + serviceClient.OnMfaEvent += ServiceClient_OnMfaEvent; + serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; + serviceClient.OnBulkServiceEvent += ServiceClient_OnBulkServiceEvent; + serviceClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; + serviceClient.OnControllerEvent += ServiceClient_OnControllerEvent; + Application.Current.Properties.Add("ServiceClient", serviceClient); + + monitorClient = new MonitorClient("ui"); + monitorClient.OnClientConnected += MonitorClient_OnClientConnected; + monitorClient.OnNotificationEvent += MonitorClient_OnInstallationNotificationEvent; + monitorClient.OnServiceStatusEvent += MonitorClient_OnServiceStatusEvent; + monitorClient.OnShutdownEvent += MonitorClient_OnShutdownEvent; + monitorClient.OnCommunicationError += MonitorClient_OnCommunicationError; + monitorClient.OnReconnectFailure += MonitorClient_OnReconnectFailure; + Application.Current.Properties.Add("MonitorClient", monitorClient); + + Application.Current.Properties.Add("Identities", new List()); + MainMenu.OnAttachmentChange += AttachmentChanged; + MainMenu.OnLogLevelChanged += LogLevelChanged; + MainMenu.OnShowBlurb += MainMenu_OnShowBlurb; + IdentityMenu.OnError += IdentityMenu_OnError; + + try { + await serviceClient.ConnectAsync(); + await serviceClient.WaitForConnectionAsync(); + } catch /*ignored for now (Exception ex) */ + { + ShowServiceNotStarted(); + serviceClient.Reconnect(); + } + + try { + await monitorClient.ConnectAsync(); + await monitorClient.WaitForConnectionAsync(); + } catch /*ignored for now (Exception ex) */ + { + monitorClient.Reconnect(); + } + + IdentityMenu.OnForgot += IdentityForgotten; + Placement(); + } + + private void MonitorClient_OnCommunicationError(object sender, Exception e) { + string msg = "Communication Error with monitor?"; + ShowError(msg, e.Message); + } + + private void MainMenu_OnShowBlurb(string message) { + _ = ShowBlurbAsync(message, "", "info"); + } + + private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) { + var found = identities.Find(id => id.Identifier == e.Identifier); + if (found == null) { + logger.Warn($"{e.Action} service event for {e.Identifier} but the provided identity identifier was not found!"); + return; + } else { + if (e.RemovedServices != null) { + foreach (var removed in e.RemovedServices) { + removeService(found, removed); + } + } + if (e.AddedServices != null) { + foreach (var added in e.AddedServices) { + addService(found, added); + } + } + LoadIdentities(true); + this.Dispatcher.Invoke(() => { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + } + + private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { + var displayMFARequired = false; + var displayMFATimout = false; + foreach (var notification in e.Notification) { + var found = identities.Find(id => id.Identifier == notification.Identifier); + if (found == null) { + logger.Warn($"{e.Op} event for {notification.Identifier} but the provided identity identifier was not found!"); + continue; + } else { + found.TimeoutMessage = notification.Message; + found.MaxTimeout = notification.MfaMaximumTimeout; + found.MinTimeout = notification.MfaMinimumTimeout; + + if (notification.MfaMinimumTimeout == 0) { + // display mfa token icon + displayMFARequired = true; + } else { + displayMFATimout = true; + } + + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + } + } + + // we may need to display mfa icon, based on the timer in UI, remove found.MFAInfo.ShowMFA setting in this function. + // the below function can show mfa icon even after user authenticates successfully, in race conditions + if (displayMFARequired || displayMFATimout) { + this.Dispatcher.Invoke(() => { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + LoadIdentities(true); + } + + private void ServiceClient_OnControllerEvent(object sender, ControllerEvent e) { + logger.Debug($"==== ControllerEvent : action:{e.Action} identifier:{e.Identifier}"); + // commenting this block, because when it receives the disconnected events, identities are disabled and + // it is not allowing me to click/perform any operation on the identity + // the color of the title is also too dark, and it is not clearly visible, when the identity is disconnected + /* if (e.Action == "connected") { var found = identities.Find(i => i.Identifier == e.Identifier); found.IsConnected = true; for (int i = 0; i < identities.Count; i++) { @@ -777,1056 +788,1096 @@ private void ServiceClient_OnControllerEvent(object sender, ControllerEvent e) { } LoadIdentities(true); } */ - } - - - string nextVersionStr = null; - private void MonitorClient_OnReconnectFailure(object sender, object e) { - logger.Trace("OnReconnectFailure triggered"); - if (nextVersionStr == null) { - // check for the current version - nextVersionStr = "checking for update"; - Version nextVersion = GithubAPI.GetVersion(GithubAPI.GetJson(GithubAPI.ProdUrl)); - nextVersionStr = nextVersion.ToString(); - Version currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; //fetch from ziti? - - int compare = currentVersion.CompareTo(nextVersion); - if (compare < 0) { - MainMenu.SetAppUpgradeAvailableText("Upgrade available: " + nextVersionStr); - logger.Info("upgrade is available. Published version: {} is newer than the current version: {}", nextVersion, currentVersion); - //UpgradeAvailable(); - } else if (compare > 0) { - logger.Info("the version installed: {0} is newer than the released version: {1}", currentVersion, nextVersion); - MainMenu.SetAppIsNewer("This version is newer than the latest: " + nextVersionStr); - } else { - logger.Info("Current version installed: {0} is the same as the latest released version {1}", currentVersion, nextVersion); - MainMenu.SetAppUpgradeAvailableText(""); - } - } - } - - private void MonitorClient_OnShutdownEvent(object sender, StatusEvent e) { - logger.Info("The monitor has indicated the application should shut down."); - this.Dispatcher.Invoke(() => { - Application.Current.Shutdown(); - }); - } - - private void MonitorClient_OnServiceStatusEvent(object sender, MonitorServiceStatusEvent evt) { - this.Dispatcher.Invoke(() => { - try { - if (evt.Message?.ToLower() == "upgrading") { - logger.Info("The monitor has indicated an upgrade is in progress. Shutting down the UI"); - UpgradeSentinel.StartUpgradeSentinel(); - - App.Current.Exit -= Current_Exit; - logger.Info("Removed Current_Exit handler"); - notifyIcon.Visible = false; - notifyIcon.Icon.Dispose(); - notifyIcon.Dispose(); - Application.Current.Shutdown(); - return; - } - SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); - MainMenu.ShowUpdateAvailable(); - logger.Debug("MonitorClient_OnServiceStatusEvent: {0}", evt.Status); - Application.Current.Properties["ReleaseStream"] = evt.ReleaseStream; - - ServiceControllerStatus status = (ServiceControllerStatus)Enum.Parse(typeof(ServiceControllerStatus), evt.Status); - - switch (status) { - case ServiceControllerStatus.Running: - logger.Info("Service is started"); - break; - case ServiceControllerStatus.Stopped: - logger.Info("Service is stopped"); - ShowServiceNotStarted(); - break; - case ServiceControllerStatus.StopPending: - logger.Info("Service is stopping..."); - - this.Dispatcher.Invoke(async () => { - SetCantDisplay("The Service is Stopping", "Please wait while the service stops", Visibility.Visible); - await WaitForServiceToStop(DateTime.Now + TimeSpan.FromSeconds(30)); - }); - break; - case ServiceControllerStatus.StartPending: - logger.Info("Service is starting..."); - break; - case ServiceControllerStatus.PausePending: - logger.Warn("UNEXPECTED STATUS: PausePending"); - break; - case ServiceControllerStatus.Paused: - logger.Warn("UNEXPECTED STATUS: Paused"); - break; - default: - logger.Warn("UNEXPECTED STATUS: {0}", evt.Status); - break; - } - } catch (Exception ex) { - logger.Warn(ex, "unexpected exception in MonitorClient_OnServiceStatusEvent? {0}", ex.Message); - } - }); - } - - public void SetAutomaticUpdateEnabled(string enabled, string url) { - this.Dispatcher.Invoke(() => { - state.AutomaticUpdatesDisabled = bool.Parse(enabled); - state.AutomaticUpdateURL = url; - }); - } - - private void MonitorClient_OnInstallationNotificationEvent(object sender, InstallationNotificationEvent evt) { - this.Dispatcher.Invoke(() => { - logger.Debug("MonitorClient_OnInstallationNotificationEvent: {0}", evt.Message); - switch (evt.Message?.ToLower()) { - case "installationupdate": - logger.Debug("Installation Update is available - {0}", evt.ZDEVersion); - var remaining = evt.InstallTime - DateTime.Now; - - state.PendingUpdate.Version = evt.ZDEVersion; - state.PendingUpdate.InstallTime = evt.InstallTime; - state.UpdateAvailable = true; - SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); - MainMenu.ShowUpdateAvailable(); - AlertCanvas.Visibility = Visibility.Visible; - - if (isToastEnabled()) { - if (!state.AutomaticUpdatesDisabled) { - if (remaining.TotalSeconds < 60) { - //this is an immediate update - show a different message - ShowToast("Ziti Desktop Edge will initiate auto installation in the next minute!"); - } else { - if (DateTime.Now > NextNotificationTime) { - ShowToast($"Update {evt.ZDEVersion} is available for Ziti Desktop Edge and will be automatically installed by " + evt.InstallTime); - NextNotificationTime = DateTime.Now + evt.NotificationDuration; - } else { - logger.Debug("Skipping notification. Time until next notification {} seconds which is at {}", (int)((NextNotificationTime - DateTime.Now).TotalSeconds), NextNotificationTime); - } - } - } else { - ShowToast("New version available", $"Version {evt.ZDEVersion} is available for Ziti Desktop Edge"); - } - SetNotifyIcon(""); - // display a tag in UI and a button for the update software - } - break; - case "configuration changed": - break; - default: - logger.Debug("unexpected event type?"); - break; - } - }); - } - - private bool isToastEnabled() { - bool result; - //only show notifications once if automatic updates are disabled - if (NotificationsShownCount == 0) { - result = true; //regardless - if never notified, always return true - } else { - result = !state.AutomaticUpdatesDisabled; - } - return result; - } - - private void ShowToast(string header, string message) { - try { - logger.Info("showing toast: {} {}", header, message); - new ToastContentBuilder() - .AddText(header) - .AddText(message) - .SetBackgroundActivation() - .Show(); - NotificationsShownCount++; - } catch { - logger.Warn("couldn't show toast: {} {}", header, message); - } - } - - private void ShowToast(string message) { - ShowToast("Important Notice", message); - } - - async private Task WaitForServiceToStop(DateTime until) { - //continually poll for the service to stop. If it is stuck - ask the user if they want to try to force - //close the service - while (DateTime.Now < until) { - await Task.Delay(2000); - MonitorServiceStatusEvent resp = await monitorClient.StatusAsync(); - if (resp.IsStopped()) { - // good - that's what we are waiting for... - return; - } else { - // bad - not stopped yet... - logger.Debug("Waiting for service to stop... Still not stopped yet. Status: {0}", resp.Status); - } - } - // real bad - means it's stuck probably. Ask the user if they want to try to force it... - logger.Warn("Waiting for service to stop... Service did not reach stopped state in the expected amount of time."); - SetCantDisplay("The Service Appears Stuck", "Would you like to try to force close the service?", Visibility.Visible); - CloseErrorButton.Content = "Force Quit"; - CloseErrorButton.Click -= CloseError; - CloseErrorButton.Click += ForceQuitButtonClick; - } - - async private void ForceQuitButtonClick(object sender, RoutedEventArgs e) { - MonitorServiceStatusEvent status = await monitorClient.ForceTerminateAsync(); - if (status.IsStopped()) { - //good - CloseErrorButton.Click += CloseError; //reset the close button... - CloseErrorButton.Click -= ForceQuitButtonClick; - } else { - //bad... - SetCantDisplay("The Service Is Still Running", "Current status is: " + status.Status, Visibility.Visible); - } - } - - async private void StartZitiService(object sender, RoutedEventArgs e) { - try { - ShowLoad("Starting", "Starting the data service"); - logger.Info("StartZitiService"); - var r = await monitorClient.StartServiceAsync(); - if (r.Code != 0) { - logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); - } else { - logger.Info("Service started!"); - //no longer used: startZitiButtonVisible = false; - CloseErrorButton.Click -= StartZitiService; - CloseError(null, null); - } - } catch (Exception ex) { - logger.Info(ex, "UNEXPECTED ERROR!"); - //no longer used: startZitiButtonVisible = false; - //CloseErrorButton.Click += StartZitiService; - CloseErrorButton.IsEnabled = true; - } - CloseErrorButton.IsEnabled = true; - // HideLoad(); - } - - private void ShowServiceNotStarted() { - TunnelConnected(false); - LoadIdentities(true); - } - - private void MonitorClient_OnClientConnected(object sender, object e) { - logger.Debug("MonitorClient_OnClientConnected"); - MainMenu.SetAppUpgradeAvailableText(""); - } - - async private void LogLevelChanged(string level) { - await serviceClient.SetLogLevelAsync(level); - await monitorClient.SetLogLevelAsync(level); - Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(level); - } - - private void IdentityMenu_OnError(string message) { - ShowError("Identity Error", message); - } - - private void ServiceClient_OnClientConnected(object sender, object e) { - this.Dispatcher.Invoke(() => { - MainMenu.Connected(); - NoServiceView.Visibility = Visibility.Collapsed; - _isServiceInError = false; - UpdateServiceView(); - SetNotifyIcon("white"); - LoadIdentities(true); - }); - } - - private void ServiceClient_OnClientDisconnected(object sender, object e) { - this.Dispatcher.Invoke(() => { - IdentityMenu.Visibility = Visibility.Collapsed; - MFASetup.Visibility = Visibility.Collapsed; - HideModal(); - MainMenu.Disconnected(); - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = (IdentityItem)IdList.Children[i]; - item.StopTimers(); - } - IdList.Children.Clear(); - if (e != null) { - logger.Debug(e.ToString()); - } - //SetCantDisplay("Start the Ziti Tunnel Service to continue"); - ShowServiceNotStarted(); - }); - } - - /// - /// If an identity gets added late, execute this. - /// - /// Do not update services for identity events - /// - /// The sending service - /// The identity event - private void ServiceClient_OnIdentityEvent(object sender, IdentityEvent e) { - if (e == null) return; - - ZitiIdentity zid = ZitiIdentity.FromClient(e.Id); - logger.Debug($"==== IdentityEvent : action:{e.Action} identifer:{e.Id.Identifier} name:{e.Id.Name} "); - - this.Dispatcher.Invoke(async () => { - if (e.Action == "added") { - var found = identities.Find(i => i.Identifier == e.Id.Identifier); - if (found == null) { - AddIdentity(zid); - LoadIdentities(true); - } else { - // means we likely are getting an update for some reason. compare the identities and use the latest info - if (zid.Name != null && zid.Name.Length > 0) found.Name = zid.Name; - if (zid.ControllerUrl != null && zid.ControllerUrl.Length > 0) found.ControllerUrl = zid.ControllerUrl; - if (zid.ContollerVersion != null && zid.ContollerVersion.Length > 0) found.ContollerVersion = zid.ContollerVersion; - found.IsEnabled = zid.IsEnabled; - found.IsMFAEnabled = e.Id.MfaEnabled; - found.IsConnected = true; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } - } else if (e.Action == "updated") { - //this indicates that all updates have been sent to the UI... wait for 2 seconds then trigger any ui updates needed - await Task.Delay(2000); - LoadIdentities(true); - } else if (e.Action == "connected") { - var found = identities.Find(i => i.Identifier == e.Id.Identifier); - found.IsConnected = true; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } else if (e.Action == "disconnected") { - var found = identities.Find(i => i.Identifier == e.Id.Identifier); - found.IsConnected = false; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } else { - IdentityForgotten(ZitiIdentity.FromClient(e.Id)); - } - }); - logger.Debug($"IDENTITY EVENT. Action: {e.Action} identifier: {zid.Identifier}"); - } - - private void ServiceClient_OnMetricsEvent(object sender, List ids) { - if (ids != null) { - long totalUp = 0; - long totalDown = 0; - foreach (var id in ids) { - //logger.Debug($"==== MetricsEvent : id {id.Name} down: {id.Metrics.Down} up:{id.Metrics.Up}"); - if (id?.Metrics != null) { - totalDown += id.Metrics.Down; - totalUp += id.Metrics.Up; - } - } - this.Dispatcher.Invoke(() => { - SetSpeed(totalUp, UploadSpeed, UploadSpeedLabel); - SetSpeed(totalDown, DownloadSpeed, DownloadSpeedLabel); - }); - } - } - - public void SetSpeed(decimal bytes, Label speed, Label speedLabel) { - int counter = 0; - while (Math.Round(bytes / 1024) >= 1) { - bytes = bytes / 1024; - counter++; - } - speed.Content = bytes.ToString("0.0"); - speedLabel.Content = suffixes[counter]; - } - - private void ServiceClient_OnServiceEvent(object sender, ServiceEvent e) { - if (e == null) return; - - logger.Debug($"==== ServiceEvent : action:{e.Action} identifier:{e.Identifier} name:{e.Service.Name} "); - var found = identities.Find(id => id.Identifier == e.Identifier); - if (found == null) { - logger.Debug($"{e.Action} service event for {e.Service.Name} but the provided identity identifier {e.Identifier} is not found!"); - return; - } - - if (e.Action == "added") { - addService(found, e.Service); - } else { - removeService(found, e.Service); - } - LoadIdentities(true); - this.Dispatcher.Invoke(() => { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - - private void addService(ZitiIdentity found, Service added) { - ZitiService zs = new ZitiService(added); - var svc = found.Services.Find(s => s.Name == zs.Name); - if (svc == null) { - logger.Debug("Service Added: " + zs.Name); - found.Services.Add(zs); - if (zs.HasFailingPostureCheck()) { - found.HasServiceFailingPostureCheck = true; - if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) { - found.IsMFANeeded = true; - } - } - } else { - logger.Debug("the service named " + zs.Name + " is already accounted for on this identity."); - } - } - - private void removeService(ZitiIdentity found, Service removed) { - logger.Debug("removing the service named: {0}", removed.Name); - found.Services.RemoveAll(s => s.Name == removed.Name); - } - - private void ServiceClient_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { - if (e == null) return; //just skip it for now... - logger.Debug($"==== TunnelStatusEvent: "); - Application.Current.Properties.Remove("CurrentTunnelStatus"); - Application.Current.Properties.Add("CurrentTunnelStatus", e.Status); - e.Status.Dump(Console.Out); - this.Dispatcher.Invoke(() => { - /*if (e.ApiVersion != DataClient.EXPECTED_API_VERSION) { + } + + + string nextVersionStr = null; + private void MonitorClient_OnReconnectFailure(object sender, object e) { + logger.Trace("OnReconnectFailure triggered"); + if (nextVersionStr == null) { + // check for the current version + nextVersionStr = "checking for update"; + Version nextVersion = GithubAPI.GetVersion(GithubAPI.GetJson(GithubAPI.ProdUrl)); + nextVersionStr = nextVersion.ToString(); + Version currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; //fetch from ziti? + + int compare = currentVersion.CompareTo(nextVersion); + if (compare < 0) { + MainMenu.SetAppUpgradeAvailableText("Upgrade available: " + nextVersionStr); + logger.Info("upgrade is available. Published version: {} is newer than the current version: {}", nextVersion, currentVersion); + //UpgradeAvailable(); + } else if (compare > 0) { + logger.Info("the version installed: {0} is newer than the released version: {1}", currentVersion, nextVersion); + MainMenu.SetAppIsNewer("This version is newer than the latest: " + nextVersionStr); + } else { + logger.Info("Current version installed: {0} is the same as the latest released version {1}", currentVersion, nextVersion); + MainMenu.SetAppUpgradeAvailableText(""); + } + } + } + + private void MonitorClient_OnShutdownEvent(object sender, StatusEvent e) { + logger.Info("The monitor has indicated the application should shut down."); + this.Dispatcher.Invoke(() => { + Application.Current.Shutdown(); + }); + } + + private void MonitorClient_OnServiceStatusEvent(object sender, MonitorServiceStatusEvent evt) { + this.Dispatcher.Invoke(() => { + try { + if (evt.Message?.ToLower() == "upgrading") { + logger.Info("The monitor has indicated an upgrade is in progress. Shutting down the UI"); + UpgradeSentinel.StartUpgradeSentinel(); + + App.Current.Exit -= Current_Exit; + logger.Info("Removed Current_Exit handler"); + notifyIcon.Visible = false; + notifyIcon.Icon.Dispose(); + notifyIcon.Dispose(); + Application.Current.Shutdown(); + return; + } + SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); + if (evt.Code != 0) { + logger.Error("CODE: " + evt.Code); + if (MainMenu.ShowUnexpectedFailure) { + ShowToast("The data channel has stopped unexpectedly", $"If this keeps happening please collect logs and report the issue.", feedbackToastButton); + } + } + MainMenu.ShowUpdateAvailable(); + logger.Debug("MonitorClient_OnServiceStatusEvent: {0}", evt.Status); + Application.Current.Properties["ReleaseStream"] = evt.ReleaseStream; + + ServiceControllerStatus status = (ServiceControllerStatus)Enum.Parse(typeof(ServiceControllerStatus), evt.Status); + + switch (status) { + case ServiceControllerStatus.Running: + logger.Info("Service is started"); + break; + case ServiceControllerStatus.Stopped: + logger.Info("Service is stopped"); + ShowServiceNotStarted(); + break; + case ServiceControllerStatus.StopPending: + logger.Info("Service is stopping..."); + + this.Dispatcher.Invoke(async () => { + SetCantDisplay("The Service is Stopping", "Please wait while the service stops", Visibility.Visible); + await WaitForServiceToStop(DateTime.Now + TimeSpan.FromSeconds(30)); + }); + break; + case ServiceControllerStatus.StartPending: + logger.Info("Service is starting..."); + break; + case ServiceControllerStatus.PausePending: + logger.Warn("UNEXPECTED STATUS: PausePending"); + break; + case ServiceControllerStatus.Paused: + logger.Warn("UNEXPECTED STATUS: Paused"); + break; + default: + logger.Warn("UNEXPECTED STATUS: {0}", evt.Status); + break; + } + } catch (Exception ex) { + logger.Warn(ex, "unexpected exception in MonitorClient_OnServiceStatusEvent? {0}", ex.Message); + } + }); + } + + public void SetAutomaticUpdateEnabled(string enabled, string url) { + this.Dispatcher.Invoke(() => { + state.AutomaticUpdatesDisabled = bool.Parse(enabled); + state.AutomaticUpdateURL = url; + }); + } + + private void MonitorClient_OnInstallationNotificationEvent(object sender, InstallationNotificationEvent evt) { + this.Dispatcher.Invoke(() => { + logger.Debug("MonitorClient_OnInstallationNotificationEvent: {0}", evt.Message); + switch (evt.Message?.ToLower()) { + case "installationupdate": + logger.Debug("Installation Update is available - {0}", evt.ZDEVersion); + var remaining = evt.InstallTime - DateTime.Now; + + state.PendingUpdate.Version = evt.ZDEVersion; + state.PendingUpdate.InstallTime = evt.InstallTime; + state.UpdateAvailable = true; + SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); + MainMenu.ShowUpdateAvailable(); + AlertCanvas.Visibility = Visibility.Visible; + + if (isToastEnabled()) { + if (!state.AutomaticUpdatesDisabled) { + if (remaining.TotalSeconds < 60) { + //this is an immediate update - show a different message + ShowToast("Ziti Desktop Edge will initiate auto installation in the next minute!"); + } else { + if (DateTime.Now > NextNotificationTime) { + ShowToast($"Update {evt.ZDEVersion} is available for Ziti Desktop Edge and will be automatically installed by " + evt.InstallTime); + NextNotificationTime = DateTime.Now + evt.NotificationDuration; + } else { + logger.Debug("Skipping notification. Time until next notification {} seconds which is at {}", (int)((NextNotificationTime - DateTime.Now).TotalSeconds), NextNotificationTime); + } + } + } else { + ShowToast("New version available", $"Version {evt.ZDEVersion} is available for Ziti Desktop Edge", null); + } + SetNotifyIcon(""); + // display a tag in UI and a button for the update software + } + break; + case "configuration changed": + break; + default: + logger.Debug("unexpected event type?"); + break; + } + }); + } + + private bool isToastEnabled() { + bool result; + //only show notifications once if automatic updates are disabled + if (NotificationsShownCount == 0) { + result = true; //regardless - if never notified, always return true + } else { + result = !state.AutomaticUpdatesDisabled; + } + return result; + } + + private void ShowToast(string header, string message, ToastButton button) { + try { + logger.Debug("showing toast: {} {}", header, message); + var builder = new ToastContentBuilder() + .AddArgument("notbutton", "click") + .AddText(header) + .AddText(message); + if (button != null) { + builder.AddButton(button); + } + builder.Show(); + NotificationsShownCount++; + } catch { + logger.Warn("couldn't show toast: {} {}", header, message); + } + } + + + private void ShowToast(string message) { + ShowToast("Important Notice", message, null); + } + + async private Task WaitForServiceToStop(DateTime until) { + //continually poll for the service to stop. If it is stuck - ask the user if they want to try to force + //close the service + while (DateTime.Now < until) { + await Task.Delay(250); + MonitorServiceStatusEvent resp = await monitorClient.StatusAsync(); + if (resp.IsStopped()) { + // good - that's what we are waiting for... + return; + } else { + // bad - not stopped yet... + logger.Debug("Waiting for service to stop... Still not stopped yet. Status: {0}", resp.Status); + } + } + // real bad - means it's stuck probably. Ask the user if they want to try to force it... + logger.Warn("Waiting for service to stop... Service did not reach stopped state in the expected amount of time."); + SetCantDisplay("The Service Appears Stuck", "Would you like to try to force close the service?", Visibility.Visible); + CloseErrorButton.Content = "Force Quit"; + CloseErrorButton.Click -= CloseError; + CloseErrorButton.Click += ForceQuitButtonClick; + } + + async private void ForceQuitButtonClick(object sender, RoutedEventArgs e) { + MonitorServiceStatusEvent status = await monitorClient.ForceTerminateAsync(); + if (status.IsStopped()) { + //good + CloseErrorButton.Click += CloseError; //reset the close button... + CloseErrorButton.Click -= ForceQuitButtonClick; + } else { + //bad... + SetCantDisplay("The Service Is Still Running", "Current status is: " + status.Status, Visibility.Visible); + } + } + + async private void StartZitiService(object sender, RoutedEventArgs e) { + try { + ShowLoad("Starting", "Starting the data service"); + logger.Info("StartZitiService"); + var r = await monitorClient.StartServiceAsync(); + if (r.Code != 0) { + logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); + } else { + logger.Info("Service started!"); + CloseErrorButton.Click -= StartZitiService; + CloseError(null, null); + } + } catch (MonitorServiceException me) { + logger.Warn("the monitor service appears offline. {0}", me); + CloseErrorButton.IsEnabled = true; + HideLoad(); + ShowError("Error Starting Service", "The monitor service is offline"); + } catch (Exception ex) { + logger.Error(ex, "UNEXPECTED ERROR!"); + CloseErrorButton.IsEnabled = true; + HideLoad(); + ShowError("Unexpected Error", "Code 2:" + ex.Message); + } + CloseErrorButton.IsEnabled = true; + // HideLoad(); + } + + private void ShowServiceNotStarted() { + TunnelConnected(false); + LoadIdentities(true); + } + + private void MonitorClient_OnClientConnected(object sender, object e) { + logger.Debug("MonitorClient_OnClientConnected"); + MainMenu.SetAppUpgradeAvailableText(""); + } + + async private Task LogLevelChanged(string level) { + int logsSet = 0; + try { + await serviceClient.SetLogLevelAsync(level); + logsSet++; + await monitorClient.SetLogLevelAsync(level); + logsSet++; + Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(level); + return true; + } catch (Exception ex) { + logger.Error(ex, "Unexpected error. logsSet: {0}", logsSet); + if (logsSet > 1) { + await ShowBlurbAsync("Unexpected error setting logs?", ""); + } else if (logsSet > 0) { + await ShowBlurbAsync("Failed to set monitor client log level", ""); + } else { + await ShowBlurbAsync("Failed to set log levels", ""); + } + } + return false; + } + + private void IdentityMenu_OnError(string message) { + ShowError("Identity Error", message); + } + + private void ServiceClient_OnClientConnected(object sender, object e) { + this.Dispatcher.Invoke(() => { + MainMenu.Connected(); + NoServiceView.Visibility = Visibility.Collapsed; + _isServiceInError = false; + UpdateServiceView(); + SetNotifyIcon("white"); + LoadIdentities(true); + }); + } + + private void ServiceClient_OnClientDisconnected(object sender, object e) { + this.Dispatcher.Invoke(() => { + IdentityMenu.Visibility = Visibility.Collapsed; + MFASetup.Visibility = Visibility.Collapsed; + HideModal(); + MainMenu.Disconnected(); + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = (IdentityItem)IdList.Children[i]; + item.StopTimers(); + } + IdList.Children.Clear(); + if (e != null) { + logger.Debug(e.ToString()); + } + //SetCantDisplay("Start the Ziti Tunnel Service to continue"); + SetNotifyIcon("red"); + ShowServiceNotStarted(); + }); + } + + /// + /// If an identity gets added late, execute this. + /// + /// Do not update services for identity events + /// + /// The sending service + /// The identity event + private void ServiceClient_OnIdentityEvent(object sender, IdentityEvent e) { + if (e == null) return; + + ZitiIdentity zid = ZitiIdentity.FromClient(e.Id); + logger.Debug($"==== IdentityEvent : action:{e.Action} identifer:{e.Id.Identifier} name:{e.Id.Name} "); + + this.Dispatcher.Invoke(async () => { + if (e.Action == "added") { + var found = identities.Find(i => i.Identifier == e.Id.Identifier); + if (found == null) { + AddIdentity(zid); + LoadIdentities(true); + } else { + // means we likely are getting an update for some reason. compare the identities and use the latest info + if (zid.Name != null && zid.Name.Length > 0) found.Name = zid.Name; + if (zid.ControllerUrl != null && zid.ControllerUrl.Length > 0) found.ControllerUrl = zid.ControllerUrl; + if (zid.ContollerVersion != null && zid.ContollerVersion.Length > 0) found.ContollerVersion = zid.ContollerVersion; + found.IsEnabled = zid.IsEnabled; + found.IsMFAEnabled = e.Id.MfaEnabled; + found.IsConnected = true; + found.NeedsExtAuth = e.Id.NeedsExtAuth; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } + } else if (e.Action == "updated") { + //this indicates that all updates have been sent to the UI... wait for 2 seconds then trigger any ui updates needed + await Task.Delay(2000); + LoadIdentities(true); + } else if (e.Action == "connected") { + var found = identities.Find(i => i.Identifier == e.Id.Identifier); + found.IsConnected = true; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } else if (e.Action == "disconnected") { + var found = identities.Find(i => i.Identifier == e.Id.Identifier); + found.IsConnected = false; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } else if (e.Action == "needs_ext_login") { + logger.Debug("needs_ext_login action received"); //handled through identity event at the moment (forever?) + } else { + logger.Warn("unexpected action received: {}", e.Action); + IdentityForgotten(ZitiIdentity.FromClient(e.Id)); + } + }); + logger.Debug($"IDENTITY EVENT. Action: {e.Action} identifier: {zid.Identifier}"); + } + + private void ServiceClient_OnMetricsEvent(object sender, List ids) { + if (ids != null) { + long totalUp = 0; + long totalDown = 0; + foreach (var id in ids) { + //logger.Debug($"==== MetricsEvent : id {id.Name} down: {id.Metrics.Down} up:{id.Metrics.Up}"); + if (id?.Metrics != null) { + totalDown += id.Metrics.Down; + totalUp += id.Metrics.Up; + } + } + this.Dispatcher.Invoke(() => { + SetSpeed(totalUp, UploadSpeed, UploadSpeedLabel); + SetSpeed(totalDown, DownloadSpeed, DownloadSpeedLabel); + }); + } + } + + public void SetSpeed(decimal bytes, Label speed, Label speedLabel) { + int counter = 0; + while (Math.Round(bytes / 1024) >= 1) { + bytes = bytes / 1024; + counter++; + } + speed.Content = bytes.ToString("0.0"); + speedLabel.Content = suffixes[counter]; + } + + private void ServiceClient_OnServiceEvent(object sender, ServiceEvent e) { + if (e == null) return; + + logger.Debug($"==== ServiceEvent : action:{e.Action} identifier:{e.Identifier} name:{e.Service.Name} "); + var found = identities.Find(id => id.Identifier == e.Identifier); + if (found == null) { + logger.Debug($"{e.Action} service event for {e.Service.Name} but the provided identity identifier {e.Identifier} is not found!"); + return; + } + + if (e.Action == "added") { + addService(found, e.Service); + } else { + removeService(found, e.Service); + } + LoadIdentities(true); + this.Dispatcher.Invoke(() => { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + + private void addService(ZitiIdentity found, Service added) { + ZitiService zs = new ZitiService(added); + var svc = found.Services.Find(s => s.Name == zs.Name); + if (svc == null) { + logger.Debug("Service Added: " + zs.Name); + found.Services.Add(zs); + if (zs.HasFailingPostureCheck()) { + found.HasServiceFailingPostureCheck = true; + if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) { + found.IsMFANeeded = true; + } + } + } else { + logger.Debug("the service named " + zs.Name + " is already accounted for on this identity."); + } + } + + private void removeService(ZitiIdentity found, Service removed) { + logger.Debug("removing the service named: {0}", removed.Name); + found.Services.RemoveAll(s => s.Name == removed.Name); + } + + private void ServiceClient_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { + if (e == null) return; //just skip it for now... + logger.Debug($"==== TunnelStatusEvent: "); + Application.Current.Properties.Remove("CurrentTunnelStatus"); + Application.Current.Properties.Add("CurrentTunnelStatus", e.Status); + e.Status.Dump(Console.Out); + this.Dispatcher.Invoke(() => { + /*if (e.ApiVersion != DataClient.EXPECTED_API_VERSION) { SetCantDisplay("Version mismatch!", "The version of the Service is not compatible", Visibility.Visible); return; }*/ - this.MainMenu.LogLevel = e.Status.LogLevel; - Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.Status.LogLevel); - - InitializeTimer((int)e.Status.Duration); - LoadStatusFromService(e.Status); - LoadIdentities(true); - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - - private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { - if (e.LogLevel != null) { - SetLogLevel_monitor(e.LogLevel); - this.Dispatcher.Invoke(() => { - this.MainMenu.LogLevel = e.LogLevel; - Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.LogLevel); - }); - } - } - - async private void SetLogLevel_monitor(string loglevel) { - await monitorClient.SetLogLevelAsync(loglevel); - } - - private void IdentityForgotten(ZitiIdentity forgotten) { - ZitiIdentity idToRemove = null; - foreach (var id in identities) { - if (id.Identifier == forgotten.Identifier) { - idToRemove = id; - break; - } - } - identities.Remove(idToRemove); - LoadIdentities(false); - } - - private void AttachmentChanged(bool attached) { - _isAttached = attached; - if (!_isAttached) { - SetLocation(); - } - Placement(); - MainMenu.Visibility = Visibility.Collapsed; - } - - private void LoadStatusFromService(TunnelStatus status) { - //clear any identities - this.identities.Clear(); - - if (status != null) { - _isServiceInError = false; - UpdateServiceView(); - NoServiceView.Visibility = Visibility.Collapsed; - if (status.Active) { - SetNotifyIcon("green"); - } else { - SetNotifyIcon("white"); - } - if (!Application.Current.Properties.Contains("ip")) { - Application.Current.Properties.Add("ip", status?.IpInfo?.Ip); - } else { - Application.Current.Properties["ip"] = status?.IpInfo?.Ip; - } - if (!Application.Current.Properties.Contains("subnet")) { - Application.Current.Properties.Add("subnet", status?.IpInfo?.Subnet); - } else { - Application.Current.Properties["subnet"] = status?.IpInfo?.Subnet; - } - if (!Application.Current.Properties.Contains("mtu")) { - Application.Current.Properties.Add("mtu", status?.IpInfo?.MTU); - } else { - Application.Current.Properties["mtu"] = status?.IpInfo?.MTU; - } - if (!Application.Current.Properties.Contains("dns")) { - Application.Current.Properties.Add("dns", status?.IpInfo?.DNS); - } else { - Application.Current.Properties["dns"] = status?.IpInfo?.DNS; - } - if (!Application.Current.Properties.Contains("dnsenabled")) { - Application.Current.Properties.Add("dnsenabled", status?.AddDns); - } else { - Application.Current.Properties["dnsenabled"] = status?.AddDns; - } - - string key = "ApiPageSize"; - if (!Application.Current.Properties.Contains(key)) { - Application.Current.Properties.Add(key, status?.ApiPageSize); - } else { - Application.Current.Properties[key] = status?.ApiPageSize; - } - - foreach (var id in status.Identities) { - updateViewWithIdentity(id); - } - LoadIdentities(true); - } else { - ShowServiceNotStarted(); - } - } - - private void updateViewWithIdentity(Identity id) { - var zid = ZitiIdentity.FromClient(id); - foreach (var i in identities) { - if (i.Identifier == zid.Identifier) { - identities.Remove(i); - break; - } - } - identities.Add(zid); - } - - private bool IsTimingOut() { - if (identities != null) { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].IsTimingOut) return true; - } - } - return false; - } - - private bool IsTimedOut() { - if (identities != null) { - return identities.Any(i => i.IsTimedOut); - } - return false; - } - - private void SetNotifyIcon(string iconPrefix) { - if (iconPrefix != "") CurrentIcon = iconPrefix; - string icon = "pack://application:,,/Assets/Images/ziti-" + CurrentIcon; - if (state.UpdateAvailable) { - icon += "-update"; - } else { - if (IsTimedOut()) { - icon += "-mfa"; - } else { - if (IsTimingOut()) { - icon += "-timer"; - } - } - } - icon += ".ico"; - var iconUri = new Uri(icon); - Stream iconStream = Application.GetResourceStream(iconUri).Stream; - notifyIcon.Icon = new Icon(iconStream); - - Application.Current.MainWindow.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconUri); - } - - private void LoadIdentities(Boolean repaint) { - this.Dispatcher.Invoke(() => { - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = (IdentityItem)IdList.Children[i]; - item.StopTimers(); - } - IdList.Children.Clear(); - IdList.Height = 0; - var desktopWorkingArea = SystemParameters.WorkArea; - if (_maxHeight > (desktopWorkingArea.Height - 10)) _maxHeight = desktopWorkingArea.Height - 10; - if (_maxHeight < 100) _maxHeight = 100; - IdList.MaxHeight = _maxHeight - 520; - ZitiIdentity[] ids = identities.OrderBy(i => (i.Name != null) ? i.Name.ToLower() : i.Name).ToArray(); - MainMenu.SetupIdList(ids); - if (ids.Length > 0 && serviceClient.Connected) { - double height = defaultHeight + (ids.Length * 60); - if (height > _maxHeight) height = _maxHeight; - this.Height = height; - IdentityMenu.SetHeight(this.Height - 160); - MainMenu.IdentitiesButton.Visibility = Visibility.Visible; - foreach (var id in ids) { - IdentityItem idItem = new IdentityItem(); - - idItem.ToggleStatus.IsEnabled = id.IsEnabled; - if (id.IsEnabled) idItem.ToggleStatus.Content = "ENABLED"; - else idItem.ToggleStatus.Content = "DISABLED"; - - idItem.Authenticate += IdItem_Authenticate; - idItem.OnStatusChanged += Id_OnStatusChanged; - idItem.Identity = id; - idItem.IdentityChanged += IdItem_IdentityChanged; - - if (repaint) idItem.RefreshUI(); - - IdList.Children.Add(idItem); - - if (IdentityMenu.Visibility == Visibility.Visible) { - if (id.Identifier == IdentityMenu.Identity.Identifier) IdentityMenu.Identity = id; - } - } - DoubleAnimation animation = new DoubleAnimation((double)(ids.Length * 64), TimeSpan.FromSeconds(.2)); - IdList.BeginAnimation(FrameworkElement.HeightProperty, animation); - IdListScroller.Visibility = Visibility.Visible; - } else { - this.Height = defaultHeight; - MainMenu.IdentitiesButton.Visibility = Visibility.Collapsed; - IdListScroller.Visibility = Visibility.Visible; - - } - AddIdButton.Visibility = Visibility.Visible; - AddIdAreaButton.Visibility = Visibility.Visible; - - Placement(); - SetNotifyIcon(""); - }); - } - - private void IdItem_IdentityChanged(ZitiIdentity identity) { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == identity.Identifier) { - identities[i] = identity; - break; - } - } - SetNotifyIcon(""); - } - - private void IdItem_Authenticate(ZitiIdentity identity) { - ShowAuthenticate(identity); - } - - private void Id_OnStatusChanged(bool attached) { - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = IdList.Children[i] as IdentityItem; - if (item.ToggleSwitch.Enabled) break; - } - } - - private void TunnelConnected(bool isConnected) { - this.Dispatcher.Invoke(() => { - if (isConnected) { - ConnectButton.Visibility = Visibility.Collapsed; - DisconnectButton.Visibility = Visibility.Visible; - MainMenu.Connected(); - HideLoad(); - } else { - ConnectButton.Visibility = Visibility.Visible; - DisconnectButton.Visibility = Visibility.Collapsed; - IdentityMenu.Visibility = Visibility.Collapsed; - MainMenu.Visibility = Visibility.Collapsed; - HideBlurb(); - MainMenu.Disconnected(); - DownloadSpeed.Content = "0.0"; - UploadSpeed.Content = "0.0"; - } - }); - } - - private void SetLocation() { - var desktopWorkingArea = SystemParameters.WorkArea; - - var height = MainView.ActualHeight; - IdentityMenu.MainHeight = MainView.ActualHeight; - MainMenu.MainHeight = MainView.ActualHeight; - - Rectangle trayRectangle = WinAPI.GetTrayRectangle(); - if (trayRectangle.Top < 20) { - this.Position = "Top"; - this.Top = desktopWorkingArea.Top + _top; - this.Left = desktopWorkingArea.Right - this.Width - _right; - Arrow.SetValue(Canvas.TopProperty, (double)0); - Arrow.SetValue(Canvas.LeftProperty, (double)185); - MainMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - } else if (trayRectangle.Left < 20) { - this.Position = "Left"; - this.Left = _left; - this.Top = desktopWorkingArea.Bottom - this.ActualHeight - 75; - Arrow.SetValue(Canvas.TopProperty, height - 200); - Arrow.SetValue(Canvas.LeftProperty, (double)0); - MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); - } else if (desktopWorkingArea.Right == (double)trayRectangle.Left) { - this.Position = "Right"; - this.Left = desktopWorkingArea.Right - this.Width - 20; - this.Top = desktopWorkingArea.Bottom - height - 75; - Arrow.SetValue(Canvas.TopProperty, height - 100); - Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); - MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); - } else { - this.Position = "Bottom"; - this.Left = desktopWorkingArea.Right - this.Width - 75; - this.Top = desktopWorkingArea.Bottom - height; - Arrow.SetValue(Canvas.TopProperty, height - 35); - Arrow.SetValue(Canvas.LeftProperty, (double)185); - MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - } - } - public void Placement() { - if (_isAttached) { - Arrow.Visibility = Visibility.Visible; - IdentityMenu.Arrow.Visibility = Visibility.Visible; - SetLocation(); - } else { - IdentityMenu.Arrow.Visibility = Visibility.Collapsed; - Arrow.Visibility = Visibility.Collapsed; - } - } - - private void OpenIdentity(ZitiIdentity identity) { - IdentityMenu.Identity = identity; - } - - private void ShowMenu(object sender, MouseButtonEventArgs e) { - MainMenu.Visibility = Visibility.Visible; - } - - async private void AddIdentity(object sender, MouseButtonEventArgs e) { - UIModel.HideOnLostFocus = false; - Microsoft.Win32.OpenFileDialog jwtDialog = new Microsoft.Win32.OpenFileDialog(); - UIModel.HideOnLostFocus = true; - jwtDialog.DefaultExt = ".jwt"; - jwtDialog.Filter = "Ziti Identities (*.jwt)|*.jwt"; - - if (jwtDialog.ShowDialog() == true) { - ShowLoad("Adding Identity", "Please wait while the identity is added"); - string fileContent = File.ReadAllText(jwtDialog.FileName); - - try { - Identity createdId = await serviceClient.AddIdentityAsync(System.IO.Path.GetFileName(jwtDialog.FileName), false, fileContent); - - if (createdId != null) { - var zid = ZitiIdentity.FromClient(createdId); - AddIdentity(zid); - LoadIdentities(true); - await serviceClient.IdentityOnOffAsync(createdId.Identifier, true); - }/* else { + this.MainMenu.LogLevel = e.Status.LogLevel; + Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.Status.LogLevel); + InitializeTimer((int)e.Status.Duration); + LoadStatusFromService(e.Status); + LoadIdentities(true); + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + + private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { + if (e.LogLevel != null) { + SetLogLevel_monitor(e.LogLevel); + this.Dispatcher.Invoke(() => { + this.MainMenu.LogLevel = e.LogLevel; + Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.LogLevel); + }); + } + } + + async private void SetLogLevel_monitor(string loglevel) { + await monitorClient.SetLogLevelAsync(loglevel); + } + + private void IdentityForgotten(ZitiIdentity forgotten) { + ZitiIdentity idToRemove = null; + foreach (var id in identities) { + if (id.Identifier == forgotten.Identifier) { + idToRemove = id; + break; + } + } + identities.Remove(idToRemove); + LoadIdentities(false); + } + + private void AttachmentChanged(bool attached) { + _isAttached = attached; + if (!_isAttached) { + SetLocation(); + } + Placement(); + MainMenu.Visibility = Visibility.Collapsed; + } + + private void LoadStatusFromService(TunnelStatus status) { + //clear any identities + this.identities.Clear(); + + if (status != null) { + _isServiceInError = false; + UpdateServiceView(); + NoServiceView.Visibility = Visibility.Collapsed; + SetNotifyIcon("green"); + if (!Application.Current.Properties.Contains("ip")) { + Application.Current.Properties.Add("ip", status?.IpInfo?.Ip); + } else { + Application.Current.Properties["ip"] = status?.IpInfo?.Ip; + } + if (!Application.Current.Properties.Contains("subnet")) { + Application.Current.Properties.Add("subnet", status?.IpInfo?.Subnet); + } else { + Application.Current.Properties["subnet"] = status?.IpInfo?.Subnet; + } + if (!Application.Current.Properties.Contains("mtu")) { + Application.Current.Properties.Add("mtu", status?.IpInfo?.MTU); + } else { + Application.Current.Properties["mtu"] = status?.IpInfo?.MTU; + } + if (!Application.Current.Properties.Contains("dns")) { + Application.Current.Properties.Add("dns", status?.IpInfo?.DNS); + } else { + Application.Current.Properties["dns"] = status?.IpInfo?.DNS; + } + if (!Application.Current.Properties.Contains("dnsenabled")) { + Application.Current.Properties.Add("dnsenabled", status?.AddDns); + } else { + Application.Current.Properties["dnsenabled"] = status?.AddDns; + } + + string key = "ApiPageSize"; + if (!Application.Current.Properties.Contains(key)) { + Application.Current.Properties.Add(key, status?.ApiPageSize); + } else { + Application.Current.Properties[key] = status?.ApiPageSize; + } + + foreach (var id in status.Identities) { + updateViewWithIdentity(id); + } + LoadIdentities(true); + } else { + ShowServiceNotStarted(); + } + } + + private void updateViewWithIdentity(Identity id) { + var zid = ZitiIdentity.FromClient(id); + foreach (var i in identities) { + if (i.Identifier == zid.Identifier) { + identities.Remove(i); + break; + } + } + identities.Add(zid); + } + + private bool IsTimingOut() { + if (identities != null) { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].IsTimingOut) return true; + } + } + return false; + } + + private bool IsTimedOut() { + if (identities != null) { + return identities.Any(i => i.IsTimedOut); + } + return false; + } + + private void SetNotifyIcon(string iconPrefix) { + if (iconPrefix != "") CurrentIcon = iconPrefix; + string icon = "pack://application:,,/Assets/Images/ziti-" + CurrentIcon; + if (state.UpdateAvailable) { + icon += "-update"; + } else { + if (IsTimedOut()) { + icon += "-mfa"; + } else { + if (IsTimingOut()) { + icon += "-timer"; + } + } + } + icon += ".ico"; + var iconUri = new Uri(icon); + Stream iconStream = Application.GetResourceStream(iconUri).Stream; + notifyIcon.Icon = new Icon(iconStream); + + Application.Current.MainWindow.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconUri); + } + + private void LoadIdentities(Boolean repaint) { + this.Dispatcher.Invoke(() => { + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = (IdentityItem)IdList.Children[i]; + item.StopTimers(); + } + IdList.Children.Clear(); + IdList.Height = 0; + var desktopWorkingArea = SystemParameters.WorkArea; + if (_maxHeight > (desktopWorkingArea.Height - 10)) _maxHeight = desktopWorkingArea.Height - 10; + if (_maxHeight < 100) _maxHeight = 100; + IdList.MaxHeight = _maxHeight - 520; + ZitiIdentity[] ids = identities.OrderBy(i => (i.Name != null) ? i.Name.ToLower() : i.Name).ToArray(); + MainMenu.SetupIdList(ids); + if (ids.Length > 0 && serviceClient.Connected) { + double height = defaultHeight + (ids.Length * 60); + if (height > _maxHeight) height = _maxHeight; + this.Height = height; + IdentityMenu.SetHeight(this.Height - 160); + MainMenu.IdentitiesButton.Visibility = Visibility.Visible; + foreach (var id in ids) { + IdentityItem idItem = new IdentityItem(); + + idItem.ToggleStatus.IsEnabled = id.IsEnabled; + if (id.IsEnabled) idItem.ToggleStatus.Content = "ENABLED"; + else idItem.ToggleStatus.Content = "DISABLED"; + + idItem.Authenticate += IdItem_Authenticate; + idItem.OnStatusChanged += Id_OnStatusChanged; + idItem.Identity = id; + idItem.IdentityChanged += IdItem_IdentityChanged; + + if (repaint) { + idItem.RefreshUI(); + } + + IdList.Children.Add(idItem); + + if (IdentityMenu.Visibility == Visibility.Visible) { + if (id.Identifier == IdentityMenu.Identity.Identifier) IdentityMenu.Identity = id; + } + } + DoubleAnimation animation = new DoubleAnimation((double)(ids.Length * 64), TimeSpan.FromSeconds(.2)); + IdList.BeginAnimation(FrameworkElement.HeightProperty, animation); + IdListScroller.Visibility = Visibility.Visible; + } else { + this.Height = defaultHeight; + MainMenu.IdentitiesButton.Visibility = Visibility.Collapsed; + IdListScroller.Visibility = Visibility.Visible; + + } + AddIdButton.Visibility = Visibility.Visible; + AddIdAreaButton.Visibility = Visibility.Visible; + + Placement(); + SetNotifyIcon(""); + }); + } + + private void IdItem_IdentityChanged(ZitiIdentity identity) { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == identity.Identifier) { + identities[i] = identity; + break; + } + } + SetNotifyIcon(""); + } + + private void IdItem_Authenticate(ZitiIdentity identity) { + ShowAuthenticate(identity); + } + + private void Id_OnStatusChanged(bool attached) { + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = IdList.Children[i] as IdentityItem; + if (item.ToggleSwitch.Enabled) break; + } + } + + private void TunnelConnected(bool isConnected) { + this.Dispatcher.Invoke(() => { + if (isConnected) { + ConnectButton.Visibility = Visibility.Collapsed; + DisconnectButton.Visibility = Visibility.Visible; + MainMenu.Connected(); + HideLoad(); + SetNotifyIcon("green"); + } else { + ConnectButton.Visibility = Visibility.Visible; + DisconnectButton.Visibility = Visibility.Collapsed; + IdentityMenu.Visibility = Visibility.Collapsed; + MainMenu.Visibility = Visibility.Collapsed; + HideBlurb(); + MainMenu.Disconnected(); + DownloadSpeed.Content = "0.0"; + UploadSpeed.Content = "0.0"; + } + }); + } + + private void SetLocation() { + var desktopWorkingArea = SystemParameters.WorkArea; + + var height = MainView.ActualHeight; + IdentityMenu.MainHeight = MainView.ActualHeight; + MainMenu.MainHeight = MainView.ActualHeight; + + Rectangle trayRectangle = WinAPI.GetTrayRectangle(); + if (trayRectangle.Top < 20) { + this.Position = "Top"; + this.Top = desktopWorkingArea.Top + _top; + this.Left = desktopWorkingArea.Right - this.Width - _right; + Arrow.SetValue(Canvas.TopProperty, (double)0); + Arrow.SetValue(Canvas.LeftProperty, (double)185); + MainMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + } else if (trayRectangle.Left < 20) { + this.Position = "Left"; + this.Left = _left; + this.Top = desktopWorkingArea.Bottom - this.ActualHeight - 75; + Arrow.SetValue(Canvas.TopProperty, height - 200); + Arrow.SetValue(Canvas.LeftProperty, (double)0); + MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); + } else if (desktopWorkingArea.Right == (double)trayRectangle.Left) { + this.Position = "Right"; + this.Left = desktopWorkingArea.Right - this.Width - 20; + this.Top = desktopWorkingArea.Bottom - height - 75; + Arrow.SetValue(Canvas.TopProperty, height - 100); + Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); + MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); + } else { + this.Position = "Bottom"; + this.Left = desktopWorkingArea.Right - this.Width - 75; + this.Top = desktopWorkingArea.Bottom - height; + Arrow.SetValue(Canvas.TopProperty, height - 35); + Arrow.SetValue(Canvas.LeftProperty, (double)185); + MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + } + } + public void Placement() { + if (_isAttached) { + Arrow.Visibility = Visibility.Visible; + IdentityMenu.Arrow.Visibility = Visibility.Visible; + SetLocation(); + } else { + IdentityMenu.Arrow.Visibility = Visibility.Collapsed; + Arrow.Visibility = Visibility.Collapsed; + } + } + + private void OpenIdentity(ZitiIdentity identity) { + IdentityMenu.Identity = identity; + } + + private void ShowMenu(object sender, MouseButtonEventArgs e) { + MainMenu.Visibility = Visibility.Visible; + } + + async private void AddIdentity(object sender, MouseButtonEventArgs e) { + UIModel.HideOnLostFocus = false; + Microsoft.Win32.OpenFileDialog jwtDialog = new Microsoft.Win32.OpenFileDialog(); + UIModel.HideOnLostFocus = true; + jwtDialog.DefaultExt = ".jwt"; + jwtDialog.Filter = "Ziti Identities (*.jwt)|*.jwt"; + + if (jwtDialog.ShowDialog() == true) { + ShowLoad("Adding Identity", "Please wait while the identity is added"); + string fileContent = File.ReadAllText(jwtDialog.FileName); + + try { + Identity createdId = await serviceClient.AddIdentityAsync(System.IO.Path.GetFileName(jwtDialog.FileName), false, fileContent); + + if (createdId != null) { + var zid = ZitiIdentity.FromClient(createdId); + AddIdentity(zid); + LoadIdentities(true); + await serviceClient.IdentityOnOffAsync(createdId.Identifier, true); + }/* else { ShowError("Identity Error", "Identity Id was null, please try again"); }*/ - } catch (ServiceException se) { - ShowError(se.Message, se.AdditionalInfo); - } catch (Exception ex) { - ShowError("Unexpected Error", "Code 2:" + ex.Message); - } - HideLoad(); - } - } - - private void OnTimedEvent(object sender, EventArgs e) { - TimeSpan span = (DateTime.Now - _startDate); - int hours = span.Hours; - int minutes = span.Minutes; - int seconds = span.Seconds; - var hoursString = (hours > 9) ? hours.ToString() : "0" + hours; - var minutesString = (minutes > 9) ? minutes.ToString() : "0" + minutes; - var secondsString = (seconds > 9) ? seconds.ToString() : "0" + seconds; - ConnectedTime.Content = hoursString + ":" + minutesString + ":" + secondsString; - } - - private void InitializeTimer(int millisAgoStarted) { - _startDate = DateTime.Now.Subtract(new TimeSpan(0, 0, 0, 0, millisAgoStarted)); - _tunnelUptimeTimer = new System.Windows.Forms.Timer(); - _tunnelUptimeTimer.Interval = 100; - _tunnelUptimeTimer.Tick += OnTimedEvent; - _tunnelUptimeTimer.Enabled = true; - _tunnelUptimeTimer.Start(); - } - - async private Task DoConnectAsync() { - try { - SetNotifyIcon("green"); - TunnelConnected(true); - - for (int i = 0; i < identities.Count; i++) { - await serviceClient.IdentityOnOffAsync(identities[i].Identifier, true); - } - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = IdList.Children[i] as IdentityItem; - item._identity.IsEnabled = true; - item.RefreshUI(); - } - } catch (ServiceException se) { - ShowError("Error Occurred", se.Message + " " + se.AdditionalInfo); - } catch (Exception ex) { - ShowError("Unexpected Error", "Code 3:" + ex.Message); - } - } - - async private void Disconnect(object sender, RoutedEventArgs e) { - try { - ShowLoad("Disabling Service", "Please wait for the service to stop."); - var r = await monitorClient.StopServiceAsync(); - if (r.Code != 0) { - logger.Warn("ERROR: Error:{0}, Message:{1}", r.Error, r.Message); - } else { - logger.Info("Service stopped!"); - } - } catch (Exception ex) { - logger.Error(ex, "unexpected error: {0}", ex.Message); - ShowError("Error Disabling Service", "An error occurred while trying to disable the data service. Is the monitor service running?"); - } - HideLoad(); - } - - internal void ShowLoad(string title, string msg) { - this.Dispatcher.Invoke(() => { - LoadingDetails.Text = msg; - LoadingTitle.Content = title; - LoadProgress.IsIndeterminate = true; - LoadingScreen.Visibility = Visibility.Visible; - UpdateLayout(); - }); - } - - internal void HideLoad() { - this.Dispatcher.Invoke(() => { - LoadingScreen.Visibility = Visibility.Collapsed; - LoadProgress.IsIndeterminate = false; - }); - } - - private void FormFadeOut_Completed(object sender, EventArgs e) { - closeCompleted = true; - } - private bool closeCompleted = false; - private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { - if (!closeCompleted) { - FormFadeOut.Begin(); - e.Cancel = true; - } - } - - public void ShowError(string title, string message) { - this.Dispatcher.Invoke(() => { - ErrorTitle.Content = title; - ErrorDetails.Text = message; - ErrorView.Visibility = Visibility.Visible; - }); - } - - private void CloseError(object sender, RoutedEventArgs e) { - this.Dispatcher.Invoke(() => { - ErrorView.Visibility = Visibility.Collapsed; - NoServiceView.Visibility = Visibility.Collapsed; - CloseErrorButton.IsEnabled = true; - }); - } - - private void CloseApp(object sender, RoutedEventArgs e) { - Application.Current.Shutdown(); - } - - private void MainUI_Deactivated(object sender, EventArgs e) { - if (this._isAttached) { + } catch (ServiceException se) { + ShowError(se.Message, se.AdditionalInfo); + } catch (Exception ex) { + ShowError("Unexpected Error", "Code 2:" + ex.Message); + } + HideLoad(); + } + } + + private void OnTimedEvent(object sender, EventArgs e) { + TimeSpan span = (DateTime.Now - _startDate); + int hours = span.Hours; + int minutes = span.Minutes; + int seconds = span.Seconds; + var hoursString = (hours > 9) ? hours.ToString() : "0" + hours; + var minutesString = (minutes > 9) ? minutes.ToString() : "0" + minutes; + var secondsString = (seconds > 9) ? seconds.ToString() : "0" + seconds; + ConnectedTime.Content = hoursString + ":" + minutesString + ":" + secondsString; + } + + private void InitializeTimer(int millisAgoStarted) { + _startDate = DateTime.Now.Subtract(new TimeSpan(0, 0, 0, 0, millisAgoStarted)); + _tunnelUptimeTimer = new System.Windows.Forms.Timer(); + _tunnelUptimeTimer.Interval = 100; + _tunnelUptimeTimer.Tick += OnTimedEvent; + _tunnelUptimeTimer.Enabled = true; + _tunnelUptimeTimer.Start(); + } + + async private Task DoConnectAsync() { + try { + SetNotifyIcon("green"); + TunnelConnected(true); + + for (int i = 0; i < identities.Count; i++) { + await serviceClient.IdentityOnOffAsync(identities[i].Identifier, true); + } + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = IdList.Children[i] as IdentityItem; + item._identity.IsEnabled = true; + item.RefreshUI(); + } + } catch (ServiceException se) { + ShowError("Error Occurred", se.Message + " " + se.AdditionalInfo); + } catch (Exception ex) { + ShowError("Unexpected Error", "Code 3:" + ex.Message); + } + } + + async private void Disconnect(object sender, RoutedEventArgs e) { + try { + ShowLoad("Disabling Service", "Please wait for the service to stop."); + var r = await monitorClient.StopServiceAsync(); + if (r.Code != 0) { + logger.Warn("ERROR: Error:{0}, Message:{1}", r.Error, r.Message); + } else { + logger.Info("Service stopped!"); + SetNotifyIcon("white"); + } + } catch (MonitorServiceException me) { + logger.Warn("the monitor service appears offline. {0}", me); + ShowError("Error Disabling Service", "The monitor service is offline"); + } catch (Exception ex) { + logger.Error(ex, "unexpected error: {0}", ex.Message); + ShowError("Error Disabling Service", "An error occurred while trying to disable the data service. Is the monitor service running?"); + } + HideLoad(); + } + + internal void ShowLoad(string title, string msg) { + this.Dispatcher.Invoke(() => { + LoadingDetails.Text = msg; + LoadingTitle.Content = title; + LoadProgress.IsIndeterminate = true; + LoadingScreen.Visibility = Visibility.Visible; + UpdateLayout(); + }); + } + + internal void HideLoad() { + this.Dispatcher.Invoke(() => { + LoadingScreen.Visibility = Visibility.Collapsed; + LoadProgress.IsIndeterminate = false; + }); + } + + private void FormFadeOut_Completed(object sender, EventArgs e) { + closeCompleted = true; + } + private bool closeCompleted = false; + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + if (!closeCompleted) { + FormFadeOut.Begin(); + e.Cancel = true; + } + } + + public void ShowError(string title, string message) { + this.Dispatcher.Invoke(() => { + ErrorTitle.Content = title; + ErrorDetails.Text = message; + ErrorView.Visibility = Visibility.Visible; + }); + } + + private void CloseError(object sender, RoutedEventArgs e) { + this.Dispatcher.Invoke(() => { + ErrorView.Visibility = Visibility.Collapsed; + NoServiceView.Visibility = Visibility.Collapsed; + CloseErrorButton.IsEnabled = true; + }); + } + + private void CloseApp(object sender, RoutedEventArgs e) { + Application.Current.Shutdown(); + } + + private void MainUI_Deactivated(object sender, EventArgs e) { + if (this._isAttached) { #if DEBUG - logger.Debug("debug is enabled - windows pinned"); + logger.Debug("debug is enabled - windows pinned"); #else this.Visibility = Visibility.Collapsed; #endif - } - } - - private void Label_MouseDoubleClick(object sender, MouseButtonEventArgs e) { - Placement(); - } - - int cur = 0; - LogLevelEnum[] levels = new LogLevelEnum[] { LogLevelEnum.FATAL, LogLevelEnum.ERROR, LogLevelEnum.WARN, LogLevelEnum.INFO, LogLevelEnum.DEBUG, LogLevelEnum.TRACE, LogLevelEnum.VERBOSE }; - public LogLevelEnum NextLevel() { - cur++; - if (cur > 6) { - cur = 0; - } - return levels[cur]; - } - - private void IdList_LayoutUpdated(object sender, EventArgs e) { - Placement(); - } - - async private void CollectLogFileClick(object sender, RoutedEventArgs e) { - await CollectLogFiles(); - } - async private Task CollectLogFiles() { - MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); - if (resp != null) { - - logger.Info("response: {0}", resp.Message); - } else { - ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback. Is the monitor service running?"); - } - } - - /// - /// Show the blurb as a growler notification - /// - /// The message to show - /// The url or action name to execute - public async Task ShowBlurbAsync(string message, string url, string level = "error") { - RedBlurb.Visibility = Visibility.Collapsed; - InfoBlurb.Visibility = Visibility.Collapsed; - if (level == "error") { - RedBlurb.Visibility = Visibility.Visible; - } else { - InfoBlurb.Visibility = Visibility.Visible; - } - Blurb.Content = message; - _blurbUrl = url; - BlurbArea.Visibility = Visibility.Visible; - BlurbArea.Opacity = 0; - BlurbArea.Margin = new Thickness(0, 0, 0, 0); - DoubleAnimation animation = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(15, 0, 15, 15), TimeSpan.FromSeconds(.3)); - BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); - BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); - await Task.Delay(5000); - HideBlurb(); - } - - /// - /// Execute the hide operation wihout an action from the growler - /// - /// The object that was clicked - /// The click event - private void DoHideBlurb(object sender, MouseButtonEventArgs e) { - HideBlurb(); - } - - /// - /// Hide the blurb area - /// - private void HideBlurb() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); - animation.Completed += HideComplete; - BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); - BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); - } - - /// - /// Hide the blurb area after the animation fades out - /// - /// The animation object - /// The completion event - private void HideComplete(object sender, EventArgs e) { - BlurbArea.Visibility = Visibility.Collapsed; - } - - /// - /// Execute a predefined action or url when the pop up is clicked - /// - /// The object that was clicked - /// The click event - private void BlurbAction(object sender, MouseButtonEventArgs e) { - if (_blurbUrl.Length > 0) { - // So this simply execute a url but you could do like if (_blurbUrl=="DoSomethingNifty") CallNifyFunction(); - if (_blurbUrl == this.RECOVER) { - this.ShowMFA(IdentityMenu.Identity, 4); - } else { - Process.Start(new ProcessStartInfo(_blurbUrl) { UseShellExecute = true }); - } - HideBlurb(); - } else { - HideBlurb(); - } - } - - private void ShowAuthenticate(ZitiIdentity identity) { - MFAAuthenticate(identity); - } - - private void ShowRecovery(ZitiIdentity identity) { - ShowMFARecoveryCodes(identity); - } - - - - - - private ICommand someCommand; - public ICommand SomeCommand { - get { - return someCommand - ?? (someCommand = new ActionCommand(() => { - if (DebugForm.Visibility == Visibility.Hidden) { - DebugForm.Visibility = Visibility.Visible; - } else { - DebugForm.Visibility = Visibility.Hidden; - } - })); - } - set { - someCommand = value; - } - } - - private void DoLoading(bool isComplete) { - if (isComplete) HideLoad(); - else ShowLoad("Loading", "Please Wait."); - } - } - - public class ActionCommand : ICommand { - private readonly Action _action; - - public ActionCommand(Action action) { - _action = action; - } - - public void Execute(object parameter) { - _action(); - } - - public bool CanExecute(object parameter) { - return true; - } + } + } + + private void Label_MouseDoubleClick(object sender, MouseButtonEventArgs e) { + Placement(); + } + + int cur = 0; + LogLevelEnum[] levels = new LogLevelEnum[] { LogLevelEnum.FATAL, LogLevelEnum.ERROR, LogLevelEnum.WARN, LogLevelEnum.INFO, LogLevelEnum.DEBUG, LogLevelEnum.TRACE, LogLevelEnum.VERBOSE }; + public LogLevelEnum NextLevel() { + cur++; + if (cur > 6) { + cur = 0; + } + return levels[cur]; + } + + private void IdList_LayoutUpdated(object sender, EventArgs e) { + Placement(); + } + + async private void CollectLogFileClick(object sender, RoutedEventArgs e) { + await CollectLogFiles(); + } + async private Task CollectLogFiles() { + MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); + if (resp != null) { + logger.Info("response: {0}", resp.Message); + } else { + ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback. Is the monitor service running?"); + } + } + + /// + /// Show the blurb as a growler notification + /// + /// The message to show + /// The url or action name to execute + public async Task ShowBlurbAsync(string message, string url, string level = "error") { + RedBlurb.Visibility = Visibility.Collapsed; + InfoBlurb.Visibility = Visibility.Collapsed; + if (level == "error") { + RedBlurb.Visibility = Visibility.Visible; + } else { + InfoBlurb.Visibility = Visibility.Visible; + } + Blurb.Content = message; + _blurbUrl = url; + BlurbArea.Visibility = Visibility.Visible; + BlurbArea.Opacity = 0; + BlurbArea.Margin = new Thickness(0, 0, 0, 0); + DoubleAnimation animation = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(15, 0, 15, 15), TimeSpan.FromSeconds(.3)); + BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); + BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); + await Task.Delay(5000); + HideBlurb(); + } + + /// + /// Execute the hide operation wihout an action from the growler + /// + /// The object that was clicked + /// The click event + private void DoHideBlurb(object sender, MouseButtonEventArgs e) { + HideBlurb(); + } + + /// + /// Hide the blurb area + /// + private void HideBlurb() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + animation.Completed += HideComplete; + BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); + BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); + } + + /// + /// Hide the blurb area after the animation fades out + /// + /// The animation object + /// The completion event + private void HideComplete(object sender, EventArgs e) { + BlurbArea.Visibility = Visibility.Collapsed; + } + + /// + /// Execute a predefined action or url when the pop up is clicked + /// + /// The object that was clicked + /// The click event + private void BlurbAction(object sender, MouseButtonEventArgs e) { + if (_blurbUrl.Length > 0) { + // So this simply execute a url but you could do like if (_blurbUrl=="DoSomethingNifty") CallNifyFunction(); + if (_blurbUrl == this.RECOVER) { + this.ShowMFA(IdentityMenu.Identity, 4); + } else { + Process.Start(new ProcessStartInfo(_blurbUrl) { UseShellExecute = true }); + } + HideBlurb(); + } else { + HideBlurb(); + } + } + + private void ShowAuthenticate(ZitiIdentity identity) { + MFAAuthenticate(identity); + } + + private void ShowRecovery(ZitiIdentity identity) { + ShowMFARecoveryCodes(identity); + } + + + + + + private ICommand someCommand; + public ICommand SomeCommand { + get { + return someCommand + ?? (someCommand = new ActionCommand(() => { + if (DebugForm.Visibility == Visibility.Hidden) { + DebugForm.Visibility = Visibility.Visible; + } else { + DebugForm.Visibility = Visibility.Hidden; + } + })); + } + set { + someCommand = value; + } + } + + private void DoLoading(bool isComplete) { + if (isComplete) HideLoad(); + else ShowLoad("Loading", "Please Wait."); + } + + private void Label_MouseDoubleClick_1(object sender, MouseButtonEventArgs e) { + ShowToast("here's a toast all rightddd..."); + } + } + + public class ActionCommand : ICommand { + private readonly Action _action; + + public ActionCommand(Action action) { + _action = action; + } + + public void Execute(object parameter) { + _action(); + } + + public bool CanExecute(object parameter) { + return true; + } #pragma warning disable CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used - public event EventHandler CanExecuteChanged; + public event EventHandler CanExecuteChanged; #pragma warning restore CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used - } + } } \ No newline at end of file diff --git a/DesktopEdge/Models/FilterData.cs b/DesktopEdge/Models/FilterData.cs index e86c46d09..83f6df504 100644 --- a/DesktopEdge/Models/FilterData.cs +++ b/DesktopEdge/Models/FilterData.cs @@ -1,38 +1,38 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ZitiDesktopEdge.Models { - public class FilterData { - - public FilterData():this("", "", "asc") { } - - public FilterData(string searchFor, string sortBy, string sortHow) { - SearchFor = searchFor; - SortBy = sortBy; - SortHow = sortHow; - } - - public string SearchFor { get; set; } - public string SortBy { get; set; } - public string SortHow { get; set; } - } -} + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZitiDesktopEdge.Models { + public class FilterData { + + public FilterData() : this("", "", "asc") { } + + public FilterData(string searchFor, string sortBy, string sortHow) { + SearchFor = searchFor; + SortBy = sortBy; + SortHow = sortHow; + } + + public string SearchFor { get; set; } + public string SortBy { get; set; } + public string SortHow { get; set; } + } +} diff --git a/DesktopEdge/Models/MFA.cs b/DesktopEdge/Models/MFA.cs index 4e8e0b6ce..548650921 100644 --- a/DesktopEdge/Models/MFA.cs +++ b/DesktopEdge/Models/MFA.cs @@ -1,29 +1,29 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ZitiDesktopEdge.Models { - public class MFA { - public string Url { get; set; } - public string[] RecoveryCodes { get; set; } - public bool IsAuthenticateda { get; set; } - } -} + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZitiDesktopEdge.Models { + public class MFA { + public string Url { get; set; } + public string[] RecoveryCodes { get; set; } + public bool IsAuthenticateda { get; set; } + } +} diff --git a/DesktopEdge/Models/MessageCount.cs b/DesktopEdge/Models/MessageCount.cs index d5905f0c7..cf3cd7f37 100644 --- a/DesktopEdge/Models/MessageCount.cs +++ b/DesktopEdge/Models/MessageCount.cs @@ -1,32 +1,32 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ZitiDesktopEdge.Models { - public class MessageCount { - - public int Total { get; set; } - public string Message { get; set; } - public override string ToString() { - return Total + " " + Message; - } - } -} + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZitiDesktopEdge.Models { + public class MessageCount { + + public int Total { get; set; } + public string Message { get; set; } + public override string ToString() { + return Total + " " + Message; + } + } +} diff --git a/DesktopEdge/Models/UIModel.cs b/DesktopEdge/Models/UIModel.cs index 5f3a04b80..cfedafdba 100644 --- a/DesktopEdge/Models/UIModel.cs +++ b/DesktopEdge/Models/UIModel.cs @@ -1,22 +1,21 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -namespace ZitiDesktopEdge.Models -{ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +namespace ZitiDesktopEdge.Models { static class UIModel { - static public bool HideOnLostFocus { get; set; } = true; - } + static public bool HideOnLostFocus { get; set; } = true; + } } diff --git a/DesktopEdge/Models/ViewState.cs b/DesktopEdge/Models/ViewState.cs index aef73e887..d93c80a39 100644 --- a/DesktopEdge/Models/ViewState.cs +++ b/DesktopEdge/Models/ViewState.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using NLog; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using NLog; using System; namespace Ziti.Desktop.Edge.Models { diff --git a/DesktopEdge/Models/ZitiIdentity.cs b/DesktopEdge/Models/ZitiIdentity.cs index dc8be6e99..29e40d72c 100644 --- a/DesktopEdge/Models/ZitiIdentity.cs +++ b/DesktopEdge/Models/ZitiIdentity.cs @@ -1,19 +1,19 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + using NLog; using System; using System.Collections.Generic; @@ -23,145 +23,148 @@ limitations under the License. using System.Threading.Tasks; namespace ZitiDesktopEdge.Models { - public class ZitiIdentity { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - public List Services { get; set; } - public string Name { get; set; } - public string ControllerUrl { get; set; } - - public string ContollerVersion { get; set; } - public bool IsEnabled { get; set; } - public string EnrollmentStatus { get; set; } - public string Status { get; set; } - public bool IsMFAEnabled { get; set; } - - public void MFADebug(string where) { - logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}"); - //logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}\n\tShowMFA\t : {ShowMFA}"); - } - - private bool mfaNeeded = false; - public bool IsMFANeeded { - get { return mfaNeeded; } - set { - mfaNeeded = value; - if (!mfaNeeded) { - IsTimingOut = false; - IsTimedOut = false; - WasFullNotified = false; - WasNotified = false; - } - } - } - public int MinTimeout { get; set; } - public int MaxTimeout { get; set; } - public DateTime LastUpdatedTime { get; set; } - public string TimeoutMessage { get; set; } - public bool WasNotified { get; set; } - public bool WasFullNotified { get; set; } - public string Fingerprint { get; set; } - public string Identifier { get; set; } - private bool isTimedOut = false; - - public SemaphoreSlim Mutex { get; } = new SemaphoreSlim(1); - public bool IsTimedOut { - get { return isTimedOut; } - set { - isTimedOut = value; - WasFullNotified = false; - } - } - public string[] RecoveryCodes { get; set; } - public bool IsTimingOut { get; set; } - public bool IsConnected { get; set; } - - - private bool svcFailingPostureCheck = false; - public bool HasServiceFailingPostureCheck { - get { - return svcFailingPostureCheck; - } - set { - logger.Info("Identity: {0} posture change. is a posture check failing: {1}", Name, !value); - svcFailingPostureCheck = value; - if (!value) { - IsMFANeeded = true; - } - } - } - - /// - /// Default constructor to support named initialization - /// - public ZitiIdentity() { - this.IsConnected = true; - this.Services = new List(); - } - - public ZitiIdentity(string Name, string ControllerUrl, bool IsEnabled, List Services) { - this.Name = Name; - this.Services = Services; - this.ControllerUrl = ControllerUrl; - this.IsEnabled = IsEnabled; - this.EnrollmentStatus = "Enrolled"; - this.Status = "Available"; - this.MaxTimeout = -1; - this.MinTimeout = -1; - this.LastUpdatedTime = DateTime.Now; - this.TimeoutMessage = ""; - this.RecoveryCodes = new string[0]; - this.IsTimingOut = false; - this.IsTimedOut = false; - this.IsConnected = true; - } - - public static ZitiIdentity FromClient(DataStructures.Identity id) { - ZitiIdentity zid = new ZitiIdentity() { - ControllerUrl = (id.Config == null) ? "" : id.Config.ztAPI, - ContollerVersion = id.ControllerVersion, - EnrollmentStatus = "status", - Fingerprint = id.FingerPrint, - Identifier = id.Identifier, - IsEnabled = id.Active, - Name = (string.IsNullOrEmpty(id.Name) ? id.FingerPrint : id.Name), - Status = id.Status, - RecoveryCodes = new string[0], - IsMFAEnabled = id.MfaEnabled, - IsMFANeeded = id.MfaNeeded, - IsTimedOut = false, - IsTimingOut = false, - MinTimeout = id.MinTimeout, - MaxTimeout = id.MaxTimeout, - LastUpdatedTime = id.MfaLastUpdatedTime, - TimeoutMessage = "", - IsConnected = true - }; - + public class ZitiIdentity { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + public List Services { get; set; } + public string Name { get; set; } + public string ControllerUrl { get; set; } + + public string ContollerVersion { get; set; } + public bool IsEnabled { get; set; } + public string EnrollmentStatus { get; set; } + public string Status { get; set; } + public bool IsMFAEnabled { get; set; } + + public void MFADebug(string where) { + logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}"); + //logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}\n\tShowMFA\t : {ShowMFA}"); + } + + private bool mfaNeeded = false; + public bool IsMFANeeded { + get { return mfaNeeded; } + set { + mfaNeeded = value; + if (!mfaNeeded) { + IsTimingOut = false; + IsTimedOut = false; + WasFullNotified = false; + WasNotified = false; + } + } + } + public int MinTimeout { get; set; } + public int MaxTimeout { get; set; } + public DateTime LastUpdatedTime { get; set; } + public string TimeoutMessage { get; set; } + public bool WasNotified { get; set; } + public bool WasFullNotified { get; set; } + public string Fingerprint { get; set; } + public string Identifier { get; set; } + private bool isTimedOut = false; + + public SemaphoreSlim Mutex { get; } = new SemaphoreSlim(1); + public bool IsTimedOut { + get { return isTimedOut; } + set { + isTimedOut = value; + WasFullNotified = false; + } + } + public string[] RecoveryCodes { get; set; } + public bool IsTimingOut { get; set; } + public bool IsConnected { get; set; } + + + private bool svcFailingPostureCheck = false; + public bool HasServiceFailingPostureCheck { + get { + return svcFailingPostureCheck; + } + set { + logger.Info("Identity: {0} posture change. is a posture check failing: {1}", Name, !value); + svcFailingPostureCheck = value; + if (!value) { + IsMFANeeded = true; + } + } + } + + public bool NeedsExtAuth { get; set; } + + /// + /// Default constructor to support named initialization + /// + public ZitiIdentity() { + this.IsConnected = true; + this.Services = new List(); + } + + public ZitiIdentity(string Name, string ControllerUrl, bool IsEnabled, List Services) { + this.Name = Name; + this.Services = Services; + this.ControllerUrl = ControllerUrl; + this.IsEnabled = IsEnabled; + this.EnrollmentStatus = "Enrolled"; + this.Status = "Available"; + this.MaxTimeout = -1; + this.MinTimeout = -1; + this.LastUpdatedTime = DateTime.Now; + this.TimeoutMessage = ""; + this.RecoveryCodes = new string[0]; + this.IsTimingOut = false; + this.IsTimedOut = false; + this.IsConnected = true; + } + + public static ZitiIdentity FromClient(DataStructures.Identity id) { + ZitiIdentity zid = new ZitiIdentity() { + ControllerUrl = (id.Config == null) ? "" : id.Config.ztAPI, + ContollerVersion = id.ControllerVersion, + EnrollmentStatus = "status", + Fingerprint = id.FingerPrint, + Identifier = id.Identifier, + IsEnabled = id.Active, + Name = (string.IsNullOrEmpty(id.Name) ? id.FingerPrint : id.Name), + Status = id.Status, + RecoveryCodes = new string[0], + IsMFAEnabled = id.MfaEnabled, + IsMFANeeded = id.MfaNeeded, + IsTimedOut = false, + IsTimingOut = false, + MinTimeout = id.MinTimeout, + MaxTimeout = id.MaxTimeout, + LastUpdatedTime = id.MfaLastUpdatedTime, + TimeoutMessage = "", + IsConnected = true, + NeedsExtAuth = id.NeedsExtAuth, + }; + #if DEBUG - zid.MFADebug("002"); + zid.MFADebug("002"); #endif - if (id.Services != null) { - foreach (var svc in id.Services) { - if (svc != null) { - var zsvc = new ZitiService(svc); - zsvc.TimeUpdated = zid.LastUpdatedTime; - zid.Services.Add(zsvc); - } - } - zid.HasServiceFailingPostureCheck = zid.Services.Any(p => !p.HasFailingPostureCheck()); - } - logger.Info("Identity: {0} updated To {1}", zid.Name, Newtonsoft.Json.JsonConvert.SerializeObject(id)); - return zid; - } - - public void ShowMFAToast(string message) { - logger.Info("Showing Notification from identity " + Name + " " + message + "."); - new Microsoft.Toolkit.Uwp.Notifications.ToastContentBuilder() - .AddText(Name + " Service Access Warning") - .AddText(message) - .AddArgument("identifier", Identifier) - .SetBackgroundActivation() - .Show(); - } - } + if (id.Services != null) { + foreach (var svc in id.Services) { + if (svc != null) { + var zsvc = new ZitiService(svc); + zsvc.TimeUpdated = zid.LastUpdatedTime; + zid.Services.Add(zsvc); + } + } + zid.HasServiceFailingPostureCheck = zid.Services.Any(p => !p.HasFailingPostureCheck()); + } + logger.Info("Identity: {0} updated To {1}", zid.Name, Newtonsoft.Json.JsonConvert.SerializeObject(id)); + return zid; + } + + public void ShowMFAToast(string message) { + logger.Info("Showing Notification from identity " + Name + " " + message + "."); + new Microsoft.Toolkit.Uwp.Notifications.ToastContentBuilder() + .AddText(Name + " Service Access Warning") + .AddText(message) + .AddArgument("identifier", Identifier) + .SetBackgroundActivation() + .Show(); + } + } } diff --git a/DesktopEdge/Models/ZitiService.cs b/DesktopEdge/Models/ZitiService.cs index 9f032f005..a156d32a6 100644 --- a/DesktopEdge/Models/ZitiService.cs +++ b/DesktopEdge/Models/ZitiService.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using NLog; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using NLog; using System; using System.Collections.Generic; using System.Linq; @@ -26,285 +26,285 @@ limitations under the License. using ZitiDesktopEdge.DataStructures; namespace ZitiDesktopEdge.Models { - public class ZitiService { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + public class ZitiService { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - public string Name { get; set; } - public string[] Protocols { get; set; } - public Address[] Addresses { get; set; } - public PortRange[] Ports { get; set; } - public PostureCheck[] PostureChecks { get; set; } - public bool OwnsIntercept { get; set; } - public string AssignedIP { get; set; } - public DateTime TimeUpdated { get; set; } - public int Timeout { get; set; } - public int TimeoutRemaining { get; set; } - public bool IsMfaReady { get; set; } + public string Name { get; set; } + public string[] Protocols { get; set; } + public Address[] Addresses { get; set; } + public PortRange[] Ports { get; set; } + public PostureCheck[] PostureChecks { get; set; } + public bool OwnsIntercept { get; set; } + public string AssignedIP { get; set; } + public DateTime TimeUpdated { get; set; } + public int Timeout { get; set; } + public int TimeoutRemaining { get; set; } + public bool IsMfaReady { get; set; } - private bool failingPostureCheck; - public bool HasFailingPostureCheck() { - return failingPostureCheck; - } - public bool IsAccessible { get; set; } + private bool failingPostureCheck; + public bool HasFailingPostureCheck() { + return failingPostureCheck; + } + public bool IsAccessible { get; set; } - public string Warning { - get { - if (this.OwnsIntercept) { - return ""; - } else { - return "this won't trigger right now"; //$"Another identity already mapped the specified hostname: {Host}.\nThis service is only available via IP"; - } - } - } + public string Warning { + get { + if (this.OwnsIntercept) { + return ""; + } else { + return "this won't trigger right now"; //$"Another identity already mapped the specified hostname: {Host}.\nThis service is only available via IP"; + } + } + } - public ZitiService() { - } + public ZitiService() { + } - public ZitiService(Service svc) { - this.Name = svc.Name; - this.AssignedIP = svc.AssignedIP; - this.Addresses = svc.Addresses; - this.Protocols = svc.Protocols == null ? null : svc.Protocols.Select(p => p.ToUpper()).ToArray(); - this.Ports = svc.Ports; - this.PostureChecks = svc.PostureChecks; - this.Timeout = svc.Timeout; - this.TimeoutRemaining = svc.TimeoutRemaining; - this.OwnsIntercept = svc.OwnsIntercept; - this.IsMfaReady = false; - this.TimeUpdated = DateTime.Now; - if (this.PostureChecks != null) { - this.failingPostureCheck = this.PostureChecks.Any(p => !p.IsPassing); - } - this.IsAccessible = svc.IsAccessible; - //commented out for now logger.Warn("SERVICE: " + this.Name + " HAS FAILING POSTURE CHECK: " + failingPostureCheck); - } + public ZitiService(Service svc) { + this.Name = svc.Name; + this.AssignedIP = svc.AssignedIP; + this.Addresses = svc.Addresses; + this.Protocols = svc.Protocols == null ? null : svc.Protocols.Select(p => p.ToUpper()).ToArray(); + this.Ports = svc.Ports; + this.PostureChecks = svc.PostureChecks; + this.Timeout = svc.Timeout; + this.TimeoutRemaining = svc.TimeoutRemaining; + this.OwnsIntercept = svc.OwnsIntercept; + this.IsMfaReady = false; + this.TimeUpdated = DateTime.Now; + if (this.PostureChecks != null) { + this.failingPostureCheck = this.PostureChecks.Any(p => !p.IsPassing); + } + this.IsAccessible = svc.IsAccessible; + //commented out for now logger.Warn("SERVICE: " + this.Name + " HAS FAILING POSTURE CHECK: " + failingPostureCheck); + } - public string WarningMessage { - get { - string message = Warning; - if (this.PostureChecks != null && this.PostureChecks.Length > 0) { - List messages = new List(); - for (int i = 0; i < this.PostureChecks.Length; i++) { - if (!this.PostureChecks[i].IsPassing) { - messages = AppendMessage(messages, this.PostureChecks[i].QueryType); - } - } - if (messages.Count > 0) { - string checks = ""; - for (int i = 0; i < messages.Count; i++) { - checks += ((i > 0) ? ", " : "") + messages[i].Total + " " + messages[i].Message; - } - message = message + " Posture Check Failing: " + checks; - return message.Trim(); - } else return message; - } else return message; - } - } - private List AppendMessage(List items, string message) { - bool found = false; - for (int i = 0; i < items.Count; i++) { - if (items[i].Message == message) { - items[i].Total++; - found = true; - } - } - if (!found) { - MessageCount count = new MessageCount(); - count.Total = 1; - count.Message = message; - items.Add(count); - } - return items; - } + public string WarningMessage { + get { + string message = Warning; + if (this.PostureChecks != null && this.PostureChecks.Length > 0) { + List messages = new List(); + for (int i = 0; i < this.PostureChecks.Length; i++) { + if (!this.PostureChecks[i].IsPassing) { + messages = AppendMessage(messages, this.PostureChecks[i].QueryType); + } + } + if (messages.Count > 0) { + string checks = ""; + for (int i = 0; i < messages.Count; i++) { + checks += ((i > 0) ? ", " : "") + messages[i].Total + " " + messages[i].Message; + } + message = message + " Posture Check Failing: " + checks; + return message.Trim(); + } else return message; + } else return message; + } + } + private List AppendMessage(List items, string message) { + bool found = false; + for (int i = 0; i < items.Count; i++) { + if (items[i].Message == message) { + items[i].Total++; + found = true; + } + } + if (!found) { + MessageCount count = new MessageCount(); + count.Total = 1; + count.Message = message; + items.Add(count); + } + return items; + } - public int WarnWidth { - get { - return (HasWarning) ? 30 : 0; - } - set { } - } + public int WarnWidth { + get { + return (HasWarning) ? 30 : 0; + } + set { } + } - public Visibility WarningVisibility { - get { - return (HasWarning) ? Visibility.Visible : Visibility.Collapsed; - } - set { } - } - public int WarningWidth { - get { - return (HasWarning) ? 20 : 0; - } - set { } - } + public Visibility WarningVisibility { + get { + return (HasWarning) ? Visibility.Visible : Visibility.Collapsed; + } + set { } + } + public int WarningWidth { + get { + return (HasWarning) ? 20 : 0; + } + set { } + } - public Visibility TimerVisibility { - get { - return (IsMfaReady && TimeoutCalculated > -1 && TimeoutCalculated <= 1200 && TimeoutCalculated > 0) ? Visibility.Visible : Visibility.Collapsed; - } - set { } - } + public Visibility TimerVisibility { + get { + return (IsMfaReady && TimeoutCalculated > -1 && TimeoutCalculated <= 1200 && TimeoutCalculated > 0) ? Visibility.Visible : Visibility.Collapsed; + } + set { } + } - public int TimeoutCalculated { - get { - if (this.TimeoutRemaining == -1 || TimeoutRemaining == 0) return this.TimeoutRemaining; - else { - TimeSpan t = (DateTime.Now - this.TimeUpdated.ToLocalTime()); - int timeout = this.TimeoutRemaining - (int)Math.Floor(t.TotalSeconds); - if (timeout < 0) timeout = 0; - return timeout; - } - } - set { } - } + public int TimeoutCalculated { + get { + if (this.TimeoutRemaining == -1 || TimeoutRemaining == 0) return this.TimeoutRemaining; + else { + TimeSpan t = (DateTime.Now - this.TimeUpdated.ToLocalTime()); + int timeout = this.TimeoutRemaining - (int)Math.Floor(t.TotalSeconds); + if (timeout < 0) timeout = 0; + return timeout; + } + } + set { } + } - public Visibility MfaVisibility { - get { - return (IsMfaReady && TimeoutCalculated > -1 && TimeoutCalculated == 0) ? Visibility.Visible : Visibility.Collapsed; - } - set { } - } - public int TimerWidth { - get { - return (TimeoutCalculated > -1 && TimeoutCalculated < 1200) ? 20 : 0; - } - set { } - } + public Visibility MfaVisibility { + get { + return (IsMfaReady && TimeoutCalculated > -1 && TimeoutCalculated == 0) ? Visibility.Visible : Visibility.Collapsed; + } + set { } + } + public int TimerWidth { + get { + return (TimeoutCalculated > -1 && TimeoutCalculated < 1200) ? 20 : 0; + } + set { } + } - public bool HasWarning { - get { - if (!this.OwnsIntercept) return true; - else { - if (this.IsAccessible) { - return false; - } else { - if (this.PostureChecks==null) { - return false; - } else { - for (int i = 0; i < this.PostureChecks.Length; i++) { - if (!this.PostureChecks[i].IsPassing) { - return true; - } - } - return false; - } - } - } - } - set { } - } + public bool HasWarning { + get { + if (!this.OwnsIntercept) return true; + else { + if (this.IsAccessible) { + return false; + } else { + if (this.PostureChecks == null) { + return false; + } else { + for (int i = 0; i < this.PostureChecks.Length; i++) { + if (!this.PostureChecks[i].IsPassing) { + return true; + } + } + return false; + } + } + } + } + set { } + } - public string ProtocolString { - get { - string toReturn = ""; - if (this.Protocols != null) { - for (int i = 0; i < this.Protocols.Length; i++) { - toReturn += ((i > 0) ? "," : "") + this.Protocols[i]; - } - } - return toReturn; - } - set { } - } + public string ProtocolString { + get { + string toReturn = ""; + if (this.Protocols != null) { + for (int i = 0; i < this.Protocols.Length; i++) { + toReturn += ((i > 0) ? "," : "") + this.Protocols[i]; + } + } + return toReturn; + } + set { } + } - public string PortString { - get { - string toReturn = ""; - if (this.Ports != null) { - for (int i = 0; i < this.Ports.Length; i++) { - toReturn += ((i > 0) ? "," : "") + this.Ports[i].ToString(); - } - } - return toReturn; - } - set { } - } + public string PortString { + get { + string toReturn = ""; + if (this.Ports != null) { + for (int i = 0; i < this.Ports.Length; i++) { + toReturn += ((i > 0) ? "," : "") + this.Ports[i].ToString(); + } + } + return toReturn; + } + set { } + } - public string AddressString { - get { - string toReturn = ""; - if (this.Addresses != null) { - for (int i = 0; i < this.Addresses.Length; i++) { - toReturn += ((i > 0) ? "," : "") + this.Addresses[i].ToString(); - } - } - return toReturn; - } - set { } - } + public string AddressString { + get { + string toReturn = ""; + if (this.Addresses != null) { + for (int i = 0; i < this.Addresses.Length; i++) { + toReturn += ((i > 0) ? "," : "") + this.Addresses[i].ToString(); + } + } + return toReturn; + } + set { } + } - private ServiceMatrix builtMatrix = null; - public ServiceMatrix Matrix { - get { - if (builtMatrix == null) { - builtMatrix = new ServiceMatrix(); - List matrix = new List(this.Protocols.Length * this.Addresses.Length * this.Ports.Length); + private ServiceMatrix builtMatrix = null; + public ServiceMatrix Matrix { + get { + if (builtMatrix == null) { + builtMatrix = new ServiceMatrix(); + List matrix = new List(this.Protocols.Length * this.Addresses.Length * this.Ports.Length); - foreach (var proto in this.Protocols) { - foreach (var addy in this.Addresses) { - foreach (var port in this.Ports) { - ServiceMatrixElement m = new ServiceMatrixElement(); - m.Ports = port.ToString(); - m.Proto = proto.ToUpper(); - m.Address = addy.Hostname; + foreach (var proto in this.Protocols) { + foreach (var addy in this.Addresses) { + foreach (var port in this.Ports) { + ServiceMatrixElement m = new ServiceMatrixElement(); + m.Ports = port.ToString(); + m.Proto = proto.ToUpper(); + m.Address = addy.Hostname; - matrix.Add(m); - } - } - } - builtMatrix.Elements = matrix; - } + matrix.Add(m); + } + } + } + builtMatrix.Elements = matrix; + } - return builtMatrix; - } - } + return builtMatrix; + } + } - public override string ToString() { - string protos = ""; - if (Protocols?.Length > 0) { - if (Protocols.Length > 1) { - protos = "[" + string.Join(",", Protocols.Select(p => p.ToString())) + "]"; - } else { - protos = Protocols[0]; - } - } - string addys = ""; - if (Addresses?.Length > 0) { - if (Addresses.Length > 1) { - addys = "[" + string.Join(",", Addresses.Select(a => a.ToString()).OrderBy(o => o)) + "]"; - } else { - addys = Addresses[0].ToString(); - } - } - string ranges = ""; - if (Ports?.Length > 0) { - if (Ports.Length > 1) { - ranges = "[" + string.Join(",", Ports.Select(a => a.ToString()).OrderBy(o => o)) + "]"; - } else { - ranges = Ports[0].ToString(); - } - } + public override string ToString() { + string protos = ""; + if (Protocols?.Length > 0) { + if (Protocols.Length > 1) { + protos = "[" + string.Join(",", Protocols.Select(p => p.ToString())) + "]"; + } else { + protos = Protocols[0]; + } + } + string addys = ""; + if (Addresses?.Length > 0) { + if (Addresses.Length > 1) { + addys = "[" + string.Join(",", Addresses.Select(a => a.ToString()).OrderBy(o => o)) + "]"; + } else { + addys = Addresses[0].ToString(); + } + } + string ranges = ""; + if (Ports?.Length > 0) { + if (Ports.Length > 1) { + ranges = "[" + string.Join(",", Ports.Select(a => a.ToString()).OrderBy(o => o)) + "]"; + } else { + ranges = Ports[0].ToString(); + } + } - return protos + ":" + addys + ":" + ranges; - } - } + return protos + ":" + addys + ":" + ranges; + } + } - public class ServiceMatrix { - public List Elements { get; internal set; } - } + public class ServiceMatrix { + public List Elements { get; internal set; } + } - public class ServiceMatrixElement { - public ServiceMatrixElement() { - Proto = ""; - Address = ""; - Ports = ""; - } + public class ServiceMatrixElement { + public ServiceMatrixElement() { + Proto = ""; + Address = ""; + Ports = ""; + } - public string Proto { get; set; } - public string Address { get; set; } - public string Ports { get; set; } + public string Proto { get; set; } + public string Address { get; set; } + public string Ports { get; set; } - public override string ToString() { - return Proto + " " + Address + " " + Ports; - } - } + public override string ToString() { + return Proto + " " + Address + " " + Ports; + } + } } diff --git a/DesktopEdge/Native/NativeMethods.cs b/DesktopEdge/Native/NativeMethods.cs index a8543304d..c2e0d78e9 100644 --- a/DesktopEdge/Native/NativeMethods.cs +++ b/DesktopEdge/Native/NativeMethods.cs @@ -1,17 +1,74 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace ZitiDesktopEdge.Native { public static class NativeMethods { private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); private const uint FILE_READ_EA = 0x0008; private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000; [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr CreateFile( [MarshalAs(UnmanagedType.LPTStr)] string filename, [MarshalAs(UnmanagedType.U4)] uint access, [MarshalAs(UnmanagedType.U4)] FileShare share, IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, IntPtr templateFile); public static string GetFinalPathName(string path) { var h = CreateFile(path, FILE_READ_EA, FileShare.ReadWrite | FileShare.Delete, IntPtr.Zero, FileMode.Open, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero); if (h == INVALID_HANDLE_VALUE) throw new Win32Exception(); try { var sb = new StringBuilder(1024); var res = GetFinalPathNameByHandle(h, sb, 1024, 0); if (res == 0) throw new Win32Exception(); return sb.ToString(); } finally { CloseHandle(h); } } } } \ No newline at end of file +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace ZitiDesktopEdge.Native { + + public static class NativeMethods { + private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + + private const uint FILE_READ_EA = 0x0008; + private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000; + + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr CreateFile( + [MarshalAs(UnmanagedType.LPTStr)] string filename, + [MarshalAs(UnmanagedType.U4)] uint access, + [MarshalAs(UnmanagedType.U4)] FileShare share, + IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero + [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, + [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, + IntPtr templateFile); + + public static string GetFinalPathName(string path) { + var h = CreateFile(path, + FILE_READ_EA, + FileShare.ReadWrite | FileShare.Delete, + IntPtr.Zero, + FileMode.Open, + FILE_FLAG_BACKUP_SEMANTICS, + IntPtr.Zero); + if (h == INVALID_HANDLE_VALUE) + throw new Win32Exception(); + + try { + var sb = new StringBuilder(1024); + var res = GetFinalPathNameByHandle(h, sb, 1024, 0); + if (res == 0) + throw new Win32Exception(); + + return sb.ToString(); + } finally { + CloseHandle(h); + } + } + } +} \ No newline at end of file diff --git a/DesktopEdge/Native/WinAPI.cs b/DesktopEdge/Native/WinAPI.cs index 02584d4eb..e2c9ecb3b 100644 --- a/DesktopEdge/Native/WinAPI.cs +++ b/DesktopEdge/Native/WinAPI.cs @@ -1,67 +1,67 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Runtime.InteropServices; -using System.Drawing; -using System.ComponentModel; - -namespace ZitiDesktopEdge { - - public class WinAPI { - public struct RECT { - public int left; - public int top; - public int right; - public int bottom; - - public override string ToString() { - return "(" + left + ", " + top + ") --> (" + right + ", " + bottom + ")"; - } - } - - [DllImport("user32.dll", CharSet = CharSet.Auto)] - public static extern IntPtr FindWindow(string strClassName, string strWindowName); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle); - - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); - - - public static IntPtr GetTrayHandle() { - IntPtr taskBarHandle = WinAPI.FindWindow("Shell_TrayWnd", null); - if (!taskBarHandle.Equals(IntPtr.Zero)) { - return WinAPI.FindWindowEx(taskBarHandle, IntPtr.Zero, "TrayNotifyWnd", IntPtr.Zero); - } - return IntPtr.Zero; - } - - public static Rectangle GetTrayRectangle() { - WinAPI.RECT rect; - WinAPI.GetWindowRect(WinAPI.GetTrayHandle(), out rect); - return new Rectangle(new Point(rect.left, rect.top), new Size((rect.right - rect.left) + 1, (rect.bottom - rect.top) + 1)); - } - } -} - - + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; +using System.Drawing; +using System.ComponentModel; + +namespace ZitiDesktopEdge { + + public class WinAPI { + public struct RECT { + public int left; + public int top; + public int right; + public int bottom; + + public override string ToString() { + return "(" + left + ", " + top + ") --> (" + right + ", " + bottom + ")"; + } + } + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr FindWindow(string strClassName, string strWindowName); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + + public static IntPtr GetTrayHandle() { + IntPtr taskBarHandle = WinAPI.FindWindow("Shell_TrayWnd", null); + if (!taskBarHandle.Equals(IntPtr.Zero)) { + return WinAPI.FindWindowEx(taskBarHandle, IntPtr.Zero, "TrayNotifyWnd", IntPtr.Zero); + } + return IntPtr.Zero; + } + + public static Rectangle GetTrayRectangle() { + WinAPI.RECT rect; + WinAPI.GetWindowRect(WinAPI.GetTrayHandle(), out rect); + return new Rectangle(new Point(rect.left, rect.top), new Size((rect.right - rect.left) + 1, (rect.bottom - rect.top) + 1)); + } + } +} + + diff --git a/DesktopEdge/Properties/AssemblyInfo.cs b/DesktopEdge/Properties/AssemblyInfo.cs index dd10fb999..1843f99ed 100644 --- a/DesktopEdge/Properties/AssemblyInfo.cs +++ b/DesktopEdge/Properties/AssemblyInfo.cs @@ -1,55 +1,55 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Windows; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Ziti Desktop Edge")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Ziti Desktop Edge")] -[assembly: AssemblyCopyright("Copyright NetFoundry © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US english -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] - - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] - - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("2.1.7.0")] -[assembly: AssemblyVersion("2.1.6.0")] -[assembly: AssemblyFileVersion("2.1.6.0")] +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Ziti Desktop Edge")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Ziti Desktop Edge")] +[assembly: AssemblyCopyright("Copyright NetFoundry © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("2.1.7.0")] +[assembly: AssemblyVersion("2.1.6.0")] +[assembly: AssemblyFileVersion("2.1.6.0")] diff --git a/DesktopEdge/Utils/UIUtils.cs b/DesktopEdge/Utils/UIUtils.cs index d42761de9..8d08b5b0e 100644 --- a/DesktopEdge/Utils/UIUtils.cs +++ b/DesktopEdge/Utils/UIUtils.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Threading.Tasks; using NLog; @@ -22,27 +22,26 @@ limitations under the License. namespace Ziti.Desktop.Edge.Utils { public class UIUtils { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public static void SetLogLevel(string level) { - try - { - Logger.Info("request to change log level received: {0}", level); - if ((""+level).ToLower() == "verbose") { - level = "trace"; - Logger.Info("request to change log level to verbose - but using trace instead"); - } - var l = LogLevel.FromString(level); - foreach (var rule in LogManager.Configuration.LoggingRules) { - rule.EnableLoggingForLevel(l); - rule.SetLoggingLevels(l, LogLevel.Fatal); - } - - LogManager.ReconfigExistingLoggers(); - Logger.Info("logger reconfigured to log at level: {0}", l); - } catch (Exception e) { - Logger.Error(e, "Failed to set log level: {0}", e.Message); - } - } - } + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public static void SetLogLevel(string level) { + try { + Logger.Info("request to change log level received: {0}", level); + if (("" + level).ToLower() == "verbose") { + level = "trace"; + Logger.Info("request to change log level to verbose - but using trace instead"); + } + var l = LogLevel.FromString(level); + foreach (var rule in LogManager.Configuration.LoggingRules) { + rule.EnableLoggingForLevel(l); + rule.SetLoggingLevels(l, LogLevel.Fatal); + } + + LogManager.ReconfigExistingLoggers(); + Logger.Info("logger reconfigured to log at level: {0}", l); + } catch (Exception e) { + Logger.Error(e, "Failed to set log level: {0}", e.Message); + } + } + } } diff --git a/DesktopEdge/Views/Controls/StyledButton.xaml.cs b/DesktopEdge/Views/Controls/StyledButton.xaml.cs index ff2d35e92..068f316fd 100644 --- a/DesktopEdge/Views/Controls/StyledButton.xaml.cs +++ b/DesktopEdge/Views/Controls/StyledButton.xaml.cs @@ -1,88 +1,88 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for StyledButton.xaml - /// - public partial class StyledButton : UserControl { - - public delegate void ClickAction(object sender, MouseButtonEventArgs e); - public event ClickAction OnClick; - private string _label = ""; - private string bgColor = "#0069FF"; - - public string BgColor { - get { return bgColor; } - set { - bgColor = value; - ButtonBgColor.Color = (Color)ColorConverter.ConvertFromString(bgColor); - } - } - - public string Label { - get { - return _label; - } - set { - this._label = value; - ButtonLabel.Content = this._label; - } - } - - public StyledButton() { - InitializeComponent(); - } - - /// - /// When the button area is entered slowly make it slightly opaque - /// - /// The button object - /// The mouse event - private void Hover(object sender, MouseEventArgs e) { - ButtonBgDarken.Opacity = 0.0; - ButtonBgDarken.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(0.2, TimeSpan.FromSeconds(.3))); - } - - /// - /// When the mouse leaves the button ara snap the opacity back to full - /// - /// The button object - /// The mouse event - private void Leave(object sender, MouseEventArgs e) { - ButtonBgDarken.Opacity = 0.2; - ButtonBgDarken.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(0.0, TimeSpan.FromSeconds(.3))); - } - - /// - /// Change the color to visualize a click event - /// - /// - /// - private void Down(object sender, MouseButtonEventArgs e) { - // ButtonBgColor.Color = Color.FromRgb(126, 180, 255); - } - - /// - /// Execute the click operation - /// - /// - /// - private void DoClick(object sender, MouseButtonEventArgs e) { - this.OnClick?.Invoke(sender, e); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for StyledButton.xaml + /// + public partial class StyledButton : UserControl { + + public delegate void ClickAction(object sender, MouseButtonEventArgs e); + public event ClickAction OnClick; + private string _label = ""; + private string bgColor = "#0069FF"; + + public string BgColor { + get { return bgColor; } + set { + bgColor = value; + ButtonBgColor.Color = (Color)ColorConverter.ConvertFromString(bgColor); + } + } + + public string Label { + get { + return _label; + } + set { + this._label = value; + ButtonLabel.Content = this._label; + } + } + + public StyledButton() { + InitializeComponent(); + } + + /// + /// When the button area is entered slowly make it slightly opaque + /// + /// The button object + /// The mouse event + private void Hover(object sender, MouseEventArgs e) { + ButtonBgDarken.Opacity = 0.0; + ButtonBgDarken.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(0.2, TimeSpan.FromSeconds(.3))); + } + + /// + /// When the mouse leaves the button ara snap the opacity back to full + /// + /// The button object + /// The mouse event + private void Leave(object sender, MouseEventArgs e) { + ButtonBgDarken.Opacity = 0.2; + ButtonBgDarken.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(0.0, TimeSpan.FromSeconds(.3))); + } + + /// + /// Change the color to visualize a click event + /// + /// + /// + private void Down(object sender, MouseButtonEventArgs e) { + // ButtonBgColor.Color = Color.FromRgb(126, 180, 255); + } + + /// + /// Execute the click operation + /// + /// + /// + private void DoClick(object sender, MouseButtonEventArgs e) { + this.OnClick?.Invoke(sender, e); + } + } +} diff --git a/DesktopEdge/Views/Controls/Toggler.xaml.cs b/DesktopEdge/Views/Controls/Toggler.xaml.cs index ce05628f5..777c37f69 100644 --- a/DesktopEdge/Views/Controls/Toggler.xaml.cs +++ b/DesktopEdge/Views/Controls/Toggler.xaml.cs @@ -1,75 +1,75 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using System.Windows.Media.Animation; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for Toggler.xaml - /// - public partial class Toggler : UserControl { - - public delegate void Toggled(bool on); - public event Toggled OnToggled; - private bool _isEnabled = false; - public Toggler() { - InitializeComponent(); - } - - public Boolean Enabled { - get { - return _isEnabled; - } - set { - _isEnabled = value; - if (_isEnabled) { - // ToggleTab.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(16, TimeSpan.FromSeconds(.3))); - // OnColor.BeginAnimation(Border.OpacityProperty, new DoubleAnimation(1.0, TimeSpan.FromSeconds(.3))); - - OnColor.Opacity = 1; - Canvas.SetLeft(ToggleTab, 16); - } else { - // ToggleTab.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); - // OnColor.BeginAnimation(Border.OpacityProperty, new DoubleAnimation(0, TimeSpan.FromSeconds(.3))); - - OnColor.Opacity = 0; - Canvas.SetLeft(ToggleTab, 1); - } - } - } - - public void Toggle() { - Enabled = !Enabled; - if (OnToggled != null) { - OnToggled(Enabled); - } - } - - private void OnToggle(object sender, RoutedEventArgs e) { - e.Handled = true; - Enabled = !Enabled; - if (OnToggled != null) { - OnToggled(Enabled); - } - } - - private void OnLoad(object sender, RoutedEventArgs e) { - if (_isEnabled) { - - } else { - - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Media.Animation; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for Toggler.xaml + /// + public partial class Toggler : UserControl { + + public delegate void Toggled(bool on); + public event Toggled OnToggled; + private bool _isEnabled = false; + public Toggler() { + InitializeComponent(); + } + + public Boolean Enabled { + get { + return _isEnabled; + } + set { + _isEnabled = value; + if (_isEnabled) { + // ToggleTab.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(16, TimeSpan.FromSeconds(.3))); + // OnColor.BeginAnimation(Border.OpacityProperty, new DoubleAnimation(1.0, TimeSpan.FromSeconds(.3))); + + OnColor.Opacity = 1; + Canvas.SetLeft(ToggleTab, 16); + } else { + // ToggleTab.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + // OnColor.BeginAnimation(Border.OpacityProperty, new DoubleAnimation(0, TimeSpan.FromSeconds(.3))); + + OnColor.Opacity = 0; + Canvas.SetLeft(ToggleTab, 1); + } + } + } + + public void Toggle() { + Enabled = !Enabled; + if (OnToggled != null) { + OnToggled(Enabled); + } + } + + private void OnToggle(object sender, RoutedEventArgs e) { + e.Handled = true; + Enabled = !Enabled; + if (OnToggled != null) { + OnToggled(Enabled); + } + } + + private void OnLoad(object sender, RoutedEventArgs e) { + if (_isEnabled) { + + } else { + + } + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/Filter.xaml.cs b/DesktopEdge/Views/ItemRenderers/Filter.xaml.cs index 1c39c4b85..4ccf671bb 100644 --- a/DesktopEdge/Views/ItemRenderers/Filter.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/Filter.xaml.cs @@ -1,106 +1,106 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using System.Timers; -using ZitiDesktopEdge.Models; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for Filter.xaml - /// - public partial class Filter :UserControl { - - public delegate void OnFilterEvent(FilterData filter); - public event OnFilterEvent OnFilter; - public string placeholder = "any text"; - private static Timer timeout; - - private FilterData filter = new FilterData("", "Name", "Asc"); - public Filter() { - InitializeComponent(); - } - - public void Clear() { - FilterFor.Text = ""; - filter.SearchFor = ""; - FilterFor.Text = placeholder; - SortWayField.SelectedIndex = 0; - SortByField.SelectedIndex = 0; - } - - private void FilterPressed(object sender, KeyEventArgs e) { - if (e.Key==Key.Enter) { - OnFilter?.Invoke(filter); - } - } - - private void FilterChanged(object sender, KeyEventArgs e) { - string search = FilterFor.Text.Trim(); - if (filter.SearchFor!=search) { - filter.SearchFor = search; - if (filter.SearchFor == placeholder) filter.SearchFor = ""; - - if (e.Key==Key.Enter) { - OnFilter?.Invoke(filter); - } else { - if (timeout != null && timeout.Enabled) { - timeout.Close(); - } - timeout = new Timer(1000); - timeout.Elapsed += OnTimedEvent; - timeout.AutoReset = false; - timeout.Enabled = true; - } - } - } - - private void SortWayChanged(object sender, SelectionChangedEventArgs e) { - ComboBoxItem selected = (ComboBoxItem)SortWayField.SelectedValue; - if (selected != null && selected.Content != null) { - if (selected.Content.ToString() != filter.SortHow) { - filter.SortHow = selected.Content.ToString(); - this.OnFilter?.Invoke(filter); - } - } - } - - private void SortByChanged(object sender, SelectionChangedEventArgs e) { - ComboBoxItem selected = (ComboBoxItem)SortByField.SelectedValue; - if (selected != null && selected.Content != null) { - if (selected.Content.ToString() != filter.SortBy) { - filter.SortBy = selected.Content.ToString(); - this.OnFilter?.Invoke(filter); - } - } - } - - private void FocusFilter(object sender, RoutedEventArgs e) { - if (FilterFor.Text==placeholder) { - FilterFor.Text = ""; - } - } - - private void FocusLostFilter(object sender, RoutedEventArgs e) { - if (FilterFor.Text.Trim() == "") { - FilterFor.Text = placeholder; - } - } - - private void OnTimedEvent(Object source, ElapsedEventArgs e) { - this.Dispatcher.Invoke(() => { - OnFilter?.Invoke(filter); - }); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Timers; +using ZitiDesktopEdge.Models; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for Filter.xaml + /// + public partial class Filter : UserControl { + + public delegate void OnFilterEvent(FilterData filter); + public event OnFilterEvent OnFilter; + public string placeholder = "any text"; + private static Timer timeout; + + private FilterData filter = new FilterData("", "Name", "Asc"); + public Filter() { + InitializeComponent(); + } + + public void Clear() { + FilterFor.Text = ""; + filter.SearchFor = ""; + FilterFor.Text = placeholder; + SortWayField.SelectedIndex = 0; + SortByField.SelectedIndex = 0; + } + + private void FilterPressed(object sender, KeyEventArgs e) { + if (e.Key == Key.Enter) { + OnFilter?.Invoke(filter); + } + } + + private void FilterChanged(object sender, KeyEventArgs e) { + string search = FilterFor.Text.Trim(); + if (filter.SearchFor != search) { + filter.SearchFor = search; + if (filter.SearchFor == placeholder) filter.SearchFor = ""; + + if (e.Key == Key.Enter) { + OnFilter?.Invoke(filter); + } else { + if (timeout != null && timeout.Enabled) { + timeout.Close(); + } + timeout = new Timer(1000); + timeout.Elapsed += OnTimedEvent; + timeout.AutoReset = false; + timeout.Enabled = true; + } + } + } + + private void SortWayChanged(object sender, SelectionChangedEventArgs e) { + ComboBoxItem selected = (ComboBoxItem)SortWayField.SelectedValue; + if (selected != null && selected.Content != null) { + if (selected.Content.ToString() != filter.SortHow) { + filter.SortHow = selected.Content.ToString(); + this.OnFilter?.Invoke(filter); + } + } + } + + private void SortByChanged(object sender, SelectionChangedEventArgs e) { + ComboBoxItem selected = (ComboBoxItem)SortByField.SelectedValue; + if (selected != null && selected.Content != null) { + if (selected.Content.ToString() != filter.SortBy) { + filter.SortBy = selected.Content.ToString(); + this.OnFilter?.Invoke(filter); + } + } + } + + private void FocusFilter(object sender, RoutedEventArgs e) { + if (FilterFor.Text == placeholder) { + FilterFor.Text = ""; + } + } + + private void FocusLostFilter(object sender, RoutedEventArgs e) { + if (FilterFor.Text.Trim() == "") { + FilterFor.Text = placeholder; + } + } + + private void OnTimedEvent(Object source, ElapsedEventArgs e) { + this.Dispatcher.Invoke(() => { + OnFilter?.Invoke(filter); + }); + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml index e08228868..a4e84ccf7 100644 --- a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml +++ b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml @@ -39,6 +39,7 @@ + @@ -46,7 +47,6 @@ - diff --git a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs index 24f838b62..1a32f5936 100644 --- a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -24,318 +24,344 @@ limitations under the License. using NLog; using SWM = System.Windows.Media; using Windows.UI.WebUI; +using Windows.Media.Protection.PlayReady; +using ZitiDesktopEdge.DataStructures; +using System.Diagnostics; namespace ZitiDesktopEdge { - /// - /// User Control to list Identities and give status - /// - public partial class IdentityItem:UserControl { - - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - public delegate void StatusChanged(bool attached); - public event StatusChanged OnStatusChanged; - public delegate void OnAuthenticate(ZitiIdentity identity); - public event OnAuthenticate Authenticate; - public delegate void OnIdentityChanged(ZitiIdentity identity); - public event OnIdentityChanged IdentityChanged; - private System.Windows.Forms.Timer _timer; - private System.Windows.Forms.Timer _timingTimer; - private float countdown = -1; - private float countdownComplete = -1; - private int available = 0; - - private static SWM.Color mfaOrange = SWM.Color.FromRgb(0xA1, 0x8B, 0x10); - private static SWM.Color defaultBlue = SWM.Color.FromRgb(0x00, 0x68, 0xF9); - private static SWM.Brush MFANeededBrush = new SWM.SolidColorBrush(mfaOrange); - private static SWM.Brush DefaultBrush = new SWM.SolidColorBrush(defaultBlue); - - public ZitiIdentity _identity; - public ZitiIdentity Identity { - get { - return _identity; - } - set { - _identity = value; - this.RefreshUI(); - } - } - - /// - /// Object constructor, setup the events for the control - /// - public IdentityItem() { - InitializeComponent(); - ToggleSwitch.OnToggled += ToggleIdentity; - } - - public void StopTimers() { - _timer?.Stop(); - _timingTimer?.Stop(); - } - - public int GetMaxTimeout() { - int maxto = -1; - for (int i=0; i<_identity.Services.Count; i++) { - ZitiService info = _identity.Services[i]; - - if (info.TimeoutCalculated > -1) { - if (info.TimeoutCalculated==0) { - available--; - } - if (info.TimeoutCalculated > maxto) maxto = info.TimeoutCalculated; - - } - logger.Trace("Max: " + _identity.Name + " "+maxto+" " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated+" "+ DateTime.Now); - } - return maxto; - } - public int GetMinTimeout() { - int minto = int.MaxValue; - for (int i = 0; i < _identity.Services.Count; i++) { - ZitiService info = _identity.Services[i]; - if (info.TimeoutCalculated > -1) { - if (info.TimeoutCalculated < minto) minto = info.TimeoutCalculated; - } - // logger.Trace("Min: " + _identity.Name + " " + minto + " " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated+" "+ DateTime.Now); - } - if (minto == int.MaxValue) minto = 0; - return minto; - } - - private void MFAEnabledAndNeeded() { - MfaRequired.Visibility = Visibility.Visible; - ServiceCountArea.Visibility = Visibility.Collapsed; - MainArea.Opacity = 0.6; - ServiceCountAreaLabel.Content = "authorize"; - - float maxto = GetMaxTimeout(); - if (maxto > -1) { - if (maxto > 0) { - if (_timer != null) _timer.Stop(); - countdownComplete = maxto; - _timer = new System.Windows.Forms.Timer(); - _timer.Interval = 1000; - _timer.Tick += TimerTicked; - _timer.Start(); - logger.Info("Timer Started for full timout in " + maxto + " seconds from identity " + _identity.Name + "."); - } else { - //if (maxto == 0) ShowTimedOut(); - } - } - float minto = GetMinTimeout(); - logger.Info("Min/Max For " + _identity.Name + " " + minto + " " + maxto); - if (minto > -1) { - if (minto > 0) { - if (_timingTimer != null) _timingTimer.Stop(); - countdown = minto; - _timingTimer = new System.Windows.Forms.Timer(); - _timingTimer.Interval = 1000; - _timingTimer.Tick += TimingTimerTick; - _timingTimer.Start(); - logger.Info("Timer Started for first timout in " + minto + " seconds from identity " + _identity.Name + " value with " + _identity.MinTimeout + "."); - } else { - if (maxto > 0) { - ShowTimeout(); - } - } - } - logger.Info("RefreshUI " + _identity.Name + " Min: " + minto + " Max: " + maxto); - } - - private void MFAEnabledAndNotNeeded() { - if (_identity.IsTimedOut) { - PostureTimedOut.Visibility = Visibility.Visible; - ServiceCountAreaLabel.Content = "authorize2"; - MainArea.Opacity = 1.0; - } else { - //MfaRequired.Visibility = Visibility.Visible; - //ServiceCountAreaLabel.Content = "authenticate1"; - //MainArea.Opacity = 0.6; - MfaRequired.Visibility = Visibility.Collapsed; - } - ServiceCountBorder.Background = DefaultBrush; - } - - private void MFANotEnabledAndNotNeeded() { - ServiceCountAreaLabel.Content = "services"; - MainArea.Opacity = 1.0; - } - - private void MFANotEnabledAndNeeded() { - ServiceCount.Content = "MFA"; - ServiceCountBorder.Background = MFANeededBrush; - ServiceCountAreaLabel.Content = "disabled"; - } - - public void RefreshUI () { - if (_identity.IsConnected) { - this.IsEnabled = true; - this.Opacity = 1.0; - } else { - this.IsEnabled = false; - this.Opacity = 0.3; - } - TimerCountdown.Visibility = Visibility.Collapsed; - PostureTimedOut.Visibility = Visibility.Collapsed; - MfaRequired.Visibility = Visibility.Collapsed; - available = _identity.Services.Count; - ToggleSwitch.Enabled = _identity.IsEnabled; - ServiceCountAreaLabel.Content = "services"; - ServiceCount.Content = _identity.Services.Count.ToString(); - MainArea.Opacity = 1.0; - ServiceCountArea.Visibility = Visibility.Visible; - ServiceCountAreaLabel.Content = "services"; - // logger.Info("RefreshUI " + _identity.Name + " MFA: "+ _identity.IsMFAEnabled+" Authenticated: "+_identity.IsAuthenticated); - - ServiceCount.Content = _identity.Services.Count.ToString(); - if (_identity.IsMFAEnabled) { - if (_identity.IsMFANeeded) { - // enabled and needed = needs to be authorized. show the lock icon and tell the user to auth - MFAEnabledAndNeeded(); - } else { - // enabled and not needed = authorized. show the services should be enabled and authorized - MFAEnabledAndNotNeeded(); - } - } else { - if (_identity.IsMFANeeded) { - // not enabled and needed = show the user the MFA disabled so they can enable it - MFANotEnabledAndNeeded(); - } else { - // normal case. means no lock icon needs to be shown - MFANotEnabledAndNotNeeded(); - } - } - - IdName.Content = _identity.Name; - IdUrl.Content = _identity.ControllerUrl; - if (_identity.ContollerVersion != null && _identity.ContollerVersion.Length > 0) IdUrl.Content = _identity.ControllerUrl + " at " + _identity.ContollerVersion; - - ToggleStatus.Content = ((ToggleSwitch.Enabled) ? "ENABLED" : "DISABLED"); - } - - private void TimingTimerTick(object sender, EventArgs e) { - available = _identity.Services.Count; - GetMaxTimeout(); - TimerCountdown.Visibility = Visibility.Collapsed; - if (countdown>-1) { - countdown--; - logger.Trace("CountDown " + countdown + " seconds from identity " + _identity.Name + "."); - if (countdown > 0) { - TimeSpan t = TimeSpan.FromSeconds(countdown); - string answer = t.Seconds + " seconds"; - if (t.Days > 0) answer = t.Days + " days " + t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; - else { - if (t.Hours > 0) answer = t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; - else { - if (t.Minutes > 0) answer = t.Minutes + " minutes " + t.Seconds + " seconds"; - } - } - if (countdown<=1200) { - ShowTimeout(); - - if (!_identity.WasNotified) { - _identity.WasNotified = true; - _identity.ShowMFAToast("The services for " + _identity.Name + " will start to time out in " + answer); - } - } - - if (available<_identity.Services.Count) MainArea.ToolTip = (_identity.Services.Count-available) + " of " + _identity.Services.Count + " services have timed out."; - else MainArea.ToolTip = "Some or all of the services will be timing out in " + answer; - } else { - ShowTimeout(); - MainArea.ToolTip = (_identity.Services.Count - available) + " of " + _identity.Services.Count+" services have timed out."; - ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; - } - } else { - ShowTimeout(); - MainArea.ToolTip = "Some or all of the services have timed out."; - ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; - } - } - - private void ShowTimeout() { - TimerCountdown.Visibility = Visibility.Visible; - ServiceCountArea.Visibility = Visibility.Collapsed; - MfaRequired.Visibility = Visibility.Collapsed; - ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; - if (!_identity.WasNotified) { - if (available < _identity.Services.Count) { - _identity.WasNotified = true; - _identity.ShowMFAToast((_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."); - } - _identity.IsTimingOut = true; - - this.IdentityChanged?.Invoke(_identity); - } - } - - private void ShowTimedOut() { - _identity.Mutex.Wait(); - Console.WriteLine(_identity.Mutex.GetHashCode()); - if (!_identity.WasFullNotified) { - _identity.WasFullNotified = true; - _identity.ShowMFAToast("All of the services with a timeout set for the identity " + _identity.Name + " have timed out"); - RefreshUI(); - if (_timer != null) _timer.Stop(); - } - _identity.Mutex.Release(); - } - - private void TimerTicked(object sender, EventArgs e) { - if (countdownComplete > -1) { - countdownComplete--; - if (countdownComplete <= 0) ShowTimedOut(); - } - } - - async private void ToggleIdentity(bool on) { - try { - if (OnStatusChanged != null) { - OnStatusChanged(on); - } - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - DataStructures.Identity id = await client.IdentityOnOffAsync(_identity.Identifier, on); - this.Identity.IsEnabled = on; - if (on) { - ToggleStatus.Content = "ENABLED"; - } else { - ToggleStatus.Content = "DISABLED"; - } - } catch (DataStructures.ServiceException se) { - MessageBox.Show(se.AdditionalInfo, se.Message); - } catch (Exception ex) { - MessageBox.Show("Error", ex.Message); - } - } - - private void Canvas_MouseEnter(object sender, MouseEventArgs e) { - OverState.Opacity = 0.2; - } - - private void Canvas_MouseLeave(object sender, MouseEventArgs e) { - OverState.Opacity = 0; - } - - private void OpenDetails(object sender, MouseButtonEventArgs e) { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - deets.SelectedIdentity = this; - deets.Identity = this.Identity; - } - - private void MFAAuthenticate(object sender, MouseButtonEventArgs e) { - this.Authenticate?.Invoke(_identity); - } - - private void ToggledSwitch(object sender, MouseButtonEventArgs e) { - ToggleSwitch.Toggle(); - } - - private void DoMFAOrOpen(object sender, MouseButtonEventArgs e) { - if (MfaRequired.Visibility==Visibility.Visible || TimerCountdown.Visibility == Visibility.Visible || PostureTimedOut.Visibility == Visibility.Visible) { - MFAAuthenticate(sender, e); - } else { - OpenDetails(sender, e); - } - } - } + /// + /// User Control to list Identities and give status + /// + public partial class IdentityItem : UserControl { + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + public delegate void StatusChanged(bool attached); + public event StatusChanged OnStatusChanged; + public delegate void OnAuthenticate(ZitiIdentity identity); + public event OnAuthenticate Authenticate; + public delegate void OnIdentityChanged(ZitiIdentity identity); + public event OnIdentityChanged IdentityChanged; + private System.Windows.Forms.Timer _timer; + private System.Windows.Forms.Timer _timingTimer; + private float countdown = -1; + private float countdownComplete = -1; + private int available = 0; + + private static SWM.Color mfaOrange = SWM.Color.FromRgb(0xA1, 0x8B, 0x10); + private static SWM.Color defaultBlue = SWM.Color.FromRgb(0x00, 0x68, 0xF9); + private static SWM.Brush MFANeededBrush = new SWM.SolidColorBrush(mfaOrange); + private static SWM.Brush DefaultBrush = new SWM.SolidColorBrush(defaultBlue); + + public ZitiIdentity _identity; + public ZitiIdentity Identity { + get { + return _identity; + } + set { + _identity = value; + this.RefreshUI(); + } + } + + /// + /// Object constructor, setup the events for the control + /// + public IdentityItem() { + InitializeComponent(); + ToggleSwitch.OnToggled += ToggleIdentity; + } + + public void StopTimers() { + _timer?.Stop(); + _timingTimer?.Stop(); + } + + public int GetMaxTimeout() { + int maxto = -1; + for (int i = 0; i < _identity.Services.Count; i++) { + ZitiService info = _identity.Services[i]; + + if (info.TimeoutCalculated > -1) { + if (info.TimeoutCalculated == 0) { + available--; + } + if (info.TimeoutCalculated > maxto) maxto = info.TimeoutCalculated; + + } + logger.Trace("Max: " + _identity.Name + " " + maxto + " " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated + " " + DateTime.Now); + } + return maxto; + } + public int GetMinTimeout() { + int minto = int.MaxValue; + for (int i = 0; i < _identity.Services.Count; i++) { + ZitiService info = _identity.Services[i]; + if (info.TimeoutCalculated > -1) { + if (info.TimeoutCalculated < minto) minto = info.TimeoutCalculated; + } + // logger.Trace("Min: " + _identity.Name + " " + minto + " " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated+" "+ DateTime.Now); + } + if (minto == int.MaxValue) minto = 0; + return minto; + } + + private void MFAEnabledAndNeeded() { + MfaRequired.Visibility = Visibility.Visible; + ServiceCountArea.Visibility = Visibility.Collapsed; + MainArea.Opacity = 0.6; + ServiceCountAreaLabel.Content = "authorize"; + + float maxto = GetMaxTimeout(); + if (maxto > -1) { + if (maxto > 0) { + if (_timer != null) _timer.Stop(); + countdownComplete = maxto; + _timer = new System.Windows.Forms.Timer(); + _timer.Interval = 1000; + _timer.Tick += TimerTicked; + _timer.Start(); + logger.Info("Timer Started for full timout in " + maxto + " seconds from identity " + _identity.Name + "."); + } else { + //if (maxto == 0) ShowTimedOut(); + } + } + float minto = GetMinTimeout(); + logger.Info("Min/Max For " + _identity.Name + " " + minto + " " + maxto); + if (minto > -1) { + if (minto > 0) { + if (_timingTimer != null) _timingTimer.Stop(); + countdown = minto; + _timingTimer = new System.Windows.Forms.Timer(); + _timingTimer.Interval = 1000; + _timingTimer.Tick += TimingTimerTick; + _timingTimer.Start(); + logger.Info("Timer Started for first timout in " + minto + " seconds from identity " + _identity.Name + " value with " + _identity.MinTimeout + "."); + } else { + if (maxto > 0) { + ShowTimeout(); + } + } + } + logger.Info("RefreshUI " + _identity.Name + " Min: " + minto + " Max: " + maxto); + } + + private void MFAEnabledAndNotNeeded() { + if (_identity.IsTimedOut) { + PostureTimedOut.Visibility = Visibility.Visible; + ServiceCountAreaLabel.Content = "authorize2"; + MainArea.Opacity = 1.0; + } else { + //MfaRequired.Visibility = Visibility.Visible; + //ServiceCountAreaLabel.Content = "authenticate1"; + //MainArea.Opacity = 0.6; + MfaRequired.Visibility = Visibility.Collapsed; + } + ServiceCountBorder.Background = DefaultBrush; + } + + private void MFANotEnabledAndNotNeeded() { + ServiceCountAreaLabel.Content = "services"; + MainArea.Opacity = 1.0; + } + + private void MFANotEnabledAndNeeded() { + ServiceCount.Content = "MFA"; + ServiceCountBorder.Background = MFANeededBrush; + ServiceCountAreaLabel.Content = "disabled"; + } + + public void RefreshUI() { + if (_identity.IsConnected) { + this.IsEnabled = true; + this.Opacity = 1.0; + } else { + this.IsEnabled = false; + this.Opacity = 0.3; + } + TimerCountdown.Visibility = Visibility.Collapsed; + PostureTimedOut.Visibility = Visibility.Collapsed; + MfaRequired.Visibility = Visibility.Collapsed; + available = _identity.Services.Count; + ToggleSwitch.Enabled = _identity.IsEnabled; + ServiceCountAreaLabel.Content = "services"; + ServiceCount.Content = _identity.Services.Count.ToString(); + MainArea.Opacity = 1.0; + ServiceCountArea.Visibility = Visibility.Visible; + ServiceCountAreaLabel.Content = "services"; + // logger.Info("RefreshUI " + _identity.Name + " MFA: "+ _identity.IsMFAEnabled+" Authenticated: "+_identity.IsAuthenticated); + + ServiceCount.Content = _identity.Services.Count.ToString(); + if (_identity.IsMFAEnabled) { + if (_identity.IsMFANeeded) { + // enabled and needed = needs to be authorized. show the lock icon and tell the user to auth + MFAEnabledAndNeeded(); + } else { + // enabled and not needed = authorized. show the services should be enabled and authorized + MFAEnabledAndNotNeeded(); + } + } else { + if (_identity.IsMFANeeded) { + // not enabled and needed = show the user the MFA disabled so they can enable it + MFANotEnabledAndNeeded(); + } else { + // normal case. means no lock icon needs to be shown + MFANotEnabledAndNotNeeded(); + } + } + + if (_identity.NeedsExtAuth) { + //show ext auth + ExtAuthRequired.Visibility = Visibility.Visible; + ServiceCountArea.Visibility = Visibility.Collapsed; + } else { + //hide ext auth + ExtAuthRequired.Visibility = Visibility.Collapsed; + ServiceCountArea.Visibility = Visibility.Visible; + } + + IdName.Content = _identity.Name; + IdUrl.Content = _identity.ControllerUrl; + if (_identity.ContollerVersion != null && _identity.ContollerVersion.Length > 0) IdUrl.Content = _identity.ControllerUrl + " at " + _identity.ContollerVersion; + + ToggleStatus.Content = ((ToggleSwitch.Enabled) ? "ENABLED" : "DISABLED"); + } + + private void TimingTimerTick(object sender, EventArgs e) { + available = _identity.Services.Count; + GetMaxTimeout(); + TimerCountdown.Visibility = Visibility.Collapsed; + if (countdown > -1) { + countdown--; + logger.Trace("CountDown " + countdown + " seconds from identity " + _identity.Name + "."); + if (countdown > 0) { + TimeSpan t = TimeSpan.FromSeconds(countdown); + string answer = t.Seconds + " seconds"; + if (t.Days > 0) answer = t.Days + " days " + t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; + else { + if (t.Hours > 0) answer = t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; + else { + if (t.Minutes > 0) answer = t.Minutes + " minutes " + t.Seconds + " seconds"; + } + } + if (countdown <= 1200) { + ShowTimeout(); + + if (!_identity.WasNotified) { + _identity.WasNotified = true; + _identity.ShowMFAToast("The services for " + _identity.Name + " will start to time out in " + answer); + } + } + + if (available < _identity.Services.Count) MainArea.ToolTip = (_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."; + else MainArea.ToolTip = "Some or all of the services will be timing out in " + answer; + } else { + ShowTimeout(); + MainArea.ToolTip = (_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."; + ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; + } + } else { + ShowTimeout(); + MainArea.ToolTip = "Some or all of the services have timed out."; + ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; + } + } + + private void ShowTimeout() { + TimerCountdown.Visibility = Visibility.Visible; + ServiceCountArea.Visibility = Visibility.Collapsed; + MfaRequired.Visibility = Visibility.Collapsed; + ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; + if (!_identity.WasNotified) { + if (available < _identity.Services.Count) { + _identity.WasNotified = true; + _identity.ShowMFAToast((_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."); + } + _identity.IsTimingOut = true; + + this.IdentityChanged?.Invoke(_identity); + } + } + + private void ShowTimedOut() { + _identity.Mutex.Wait(); + Console.WriteLine(_identity.Mutex.GetHashCode()); + if (!_identity.WasFullNotified) { + _identity.WasFullNotified = true; + _identity.ShowMFAToast("All of the services with a timeout set for the identity " + _identity.Name + " have timed out"); + RefreshUI(); + if (_timer != null) _timer.Stop(); + } + _identity.Mutex.Release(); + } + + private void TimerTicked(object sender, EventArgs e) { + if (countdownComplete > -1) { + countdownComplete--; + if (countdownComplete <= 0) ShowTimedOut(); + } + } + + async private void ToggleIdentity(bool on) { + try { + if (OnStatusChanged != null) { + OnStatusChanged(on); + } + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + DataStructures.Identity id = await client.IdentityOnOffAsync(_identity.Identifier, on); + this.Identity.IsEnabled = on; + if (on) { + ToggleStatus.Content = "ENABLED"; + } else { + ToggleStatus.Content = "DISABLED"; + } + } catch (DataStructures.ServiceException se) { + MessageBox.Show(se.AdditionalInfo, se.Message); + } catch (Exception ex) { + MessageBox.Show("Error", ex.Message); + } + } + + private void Canvas_MouseEnter(object sender, MouseEventArgs e) { + OverState.Opacity = 0.2; + } + + private void Canvas_MouseLeave(object sender, MouseEventArgs e) { + OverState.Opacity = 0; + } + + private void OpenDetails(object sender, MouseButtonEventArgs e) { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + deets.SelectedIdentity = this; + deets.Identity = this.Identity; + } + + private void MFAAuthenticate(object sender, MouseButtonEventArgs e) { + this.Authenticate?.Invoke(_identity); + } + + private void ToggledSwitch(object sender, MouseButtonEventArgs e) { + ToggleSwitch.Toggle(); + } + + private void DoMFAOrOpen(object sender, MouseButtonEventArgs e) { + if (MfaRequired.Visibility == Visibility.Visible || TimerCountdown.Visibility == Visibility.Visible || PostureTimedOut.Visibility == Visibility.Visible) { + MFAAuthenticate(sender, e); + } else if (ExtAuthRequired.Visibility == Visibility.Visible) { + CompleteExtAuth(sender, e); + } else { + OpenDetails(sender, e); + } + } + + async private void CompleteExtAuth(object sender, MouseButtonEventArgs e) { + try { + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + ExternalAuthLoginResponse resp = await client.ExternalAuthLogin(_identity.Identifier); + Console.WriteLine(resp.Data.url); + Process.Start(resp.Data.url); + } catch (Exception ex) { + logger.Error("unexpected error!", ex); + } + } + } } diff --git a/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml.cs index 243cb4c94..657b811d6 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml.cs @@ -1,60 +1,60 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MenuItem.xaml - /// - public partial class MenuEditItem: UserControl { - - private string _label = ""; - - public string Label { - get { - return _label; - } - set { - this._label = value; - MainLabel.Text = this._label; - } - } - public string Value { - get { - return MainEdit.Text; - } - set { - MainEdit.Text = value; - } - } - public bool IsLocked { - get { - return MainEdit.IsReadOnly; - } - set { - MainEdit.IsReadOnly = value; - } - } - - public MenuEditItem() { - InitializeComponent(); - } - - private void MainEdit_PreviewMouseUp(object sender, MouseButtonEventArgs e) { - var textbox = (sender as TextBox); - textbox.SelectAll(); - - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MenuItem.xaml + /// + public partial class MenuEditItem : UserControl { + + private string _label = ""; + + public string Label { + get { + return _label; + } + set { + this._label = value; + MainLabel.Text = this._label; + } + } + public string Value { + get { + return MainEdit.Text; + } + set { + MainEdit.Text = value; + } + } + public bool IsLocked { + get { + return MainEdit.IsReadOnly; + } + set { + MainEdit.IsReadOnly = value; + } + } + + public MenuEditItem() { + InitializeComponent(); + } + + private void MainEdit_PreviewMouseUp(object sender, MouseButtonEventArgs e) { + var textbox = (sender as TextBox); + textbox.SelectAll(); + + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml.cs b/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml.cs index 78f8edd09..06a5bb972 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml.cs @@ -1,70 +1,70 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MenuItem.xaml - /// - public partial class MenuEditSearch : UserControl { - - public delegate void OnFilter(string filter); - public event OnFilter Filter; - - private string _label = ""; - - public string Label { - get { - return _label; - } - set { - this._label = value; - MainLabel.Text = this._label; - } - } - public string Value { - get { - return MainEdit.Text; - } - set { - MainEdit.Text = value; - } - } - - public MenuEditSearch() { - InitializeComponent(); - } - - private void MainEdit_KeyUp(object sender, KeyEventArgs e) { - if (MainEdit.Text.Trim().Length>0) { - ClearButton.Content = "clear"; - } else { - ClearButton.Content = "search"; - } - Filter(MainEdit.Text); - } - - private void Label_MouseUp(object sender, MouseButtonEventArgs e) { - MainEdit.Text = ""; - ClearButton.Content = "search"; - Filter(MainEdit.Text); - } - - public void Clear() { - MainEdit.Text = ""; - ClearButton.Content = "search"; - Filter(MainEdit.Text); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MenuItem.xaml + /// + public partial class MenuEditSearch : UserControl { + + public delegate void OnFilter(string filter); + public event OnFilter Filter; + + private string _label = ""; + + public string Label { + get { + return _label; + } + set { + this._label = value; + MainLabel.Text = this._label; + } + } + public string Value { + get { + return MainEdit.Text; + } + set { + MainEdit.Text = value; + } + } + + public MenuEditSearch() { + InitializeComponent(); + } + + private void MainEdit_KeyUp(object sender, KeyEventArgs e) { + if (MainEdit.Text.Trim().Length > 0) { + ClearButton.Content = "clear"; + } else { + ClearButton.Content = "search"; + } + Filter(MainEdit.Text); + } + + private void Label_MouseUp(object sender, MouseButtonEventArgs e) { + MainEdit.Text = ""; + ClearButton.Content = "search"; + Filter(MainEdit.Text); + } + + public void Clear() { + MainEdit.Text = ""; + ClearButton.Content = "search"; + Filter(MainEdit.Text); + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/MenuEditToggle.xaml.cs b/DesktopEdge/Views/ItemRenderers/MenuEditToggle.xaml.cs index ffbc2da07..86d745bb7 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuEditToggle.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/MenuEditToggle.xaml.cs @@ -1,78 +1,78 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using ZitiDesktopEdge.Models; -using ZitiDesktopEdge.ServiceClient; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MenuItem.xaml - /// - public partial class MenuEditToggle: UserControl { - - public delegate void OnToggle(bool isOn); - public event OnToggle Toggle; - public delegate void OnAuthenticate(); - public event OnAuthenticate Authenticate; - public delegate void OnRecovery(); - public event OnRecovery Recovery; - private ZitiIdentity _identity; - - private string _label = ""; - - public string Label { - get { - return _label; - } - set { - this._label = value; - this.MainLabel.Text = this._label; - } - } - public bool IsOn { - get { - return ToggleField.Enabled; - } - set { - ToggleField.Enabled = value; - } - } - - public ZitiIdentity Identity { - get { - return _identity; - } - set { - _identity = value; - } - } - - public MenuEditToggle() { - InitializeComponent(); - } - - public void Toggled(Boolean isOn) { - this.ToggleField.Enabled = isOn; - this.Toggle?.Invoke(isOn); - } - - private void MFAAuthenticate(object sender, MouseButtonEventArgs e) { - this.Authenticate?.Invoke(); - } - - private void MFARecovery(object sender, MouseButtonEventArgs e) { - this.Recovery?.Invoke(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using ZitiDesktopEdge.Models; +using ZitiDesktopEdge.ServiceClient; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MenuItem.xaml + /// + public partial class MenuEditToggle : UserControl { + + public delegate void OnToggle(bool isOn); + public event OnToggle Toggle; + public delegate void OnAuthenticate(); + public event OnAuthenticate Authenticate; + public delegate void OnRecovery(); + public event OnRecovery Recovery; + private ZitiIdentity _identity; + + private string _label = ""; + + public string Label { + get { + return _label; + } + set { + this._label = value; + this.MainLabel.Text = this._label; + } + } + public bool IsOn { + get { + return ToggleField.Enabled; + } + set { + ToggleField.Enabled = value; + } + } + + public ZitiIdentity Identity { + get { + return _identity; + } + set { + _identity = value; + } + } + + public MenuEditToggle() { + InitializeComponent(); + } + + public void Toggled(Boolean isOn) { + this.ToggleField.Enabled = isOn; + this.Toggle?.Invoke(isOn); + } + + private void MFAAuthenticate(object sender, MouseButtonEventArgs e) { + this.Authenticate?.Invoke(); + } + + private void MFARecovery(object sender, MouseButtonEventArgs e) { + this.Recovery?.Invoke(); + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs index 0b72af20e..bceb6b6fa 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs @@ -1,70 +1,70 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using ZitiDesktopEdge.Models; -using ZitiDesktopEdge.DataStructures; -using ZitiDesktopEdge.ServiceClient; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MenuItem.xaml - /// - public partial class MenuIdentityItem : UserControl { - - private string _label = ""; - private ZitiIdentity _identity; - - public string Label { - get { - return _label; - } - set { - this._label = value; - MainLabel.Text = this._label; - } - } - - public ZitiIdentity Identity { - get { - return _identity; - } - set { - _identity = value; - } - } - - public MenuIdentityItem() { - InitializeComponent(); - ToggleSwitch.OnToggled += ToggleIdentity; - } - - async private void ToggleIdentity(bool on) { - try { - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - DataStructures.Identity id = await client.IdentityOnOffAsync(_identity.Identifier, on); - this.Identity.IsEnabled = on; - } catch (DataStructures.ServiceException se) { - MessageBox.Show(se.AdditionalInfo, se.Message); - } catch (Exception ex) { - MessageBox.Show("Error", ex.Message); - } - } - - private void ShowIdentity(object sender, MouseButtonEventArgs e) { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - deets.SelectedIdentityMenu = this; - deets.Identity = this.Identity; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using ZitiDesktopEdge.Models; +using ZitiDesktopEdge.DataStructures; +using ZitiDesktopEdge.ServiceClient; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MenuItem.xaml + /// + public partial class MenuIdentityItem : UserControl { + + private string _label = ""; + private ZitiIdentity _identity; + + public string Label { + get { + return _label; + } + set { + this._label = value; + MainLabel.Text = this._label; + } + } + + public ZitiIdentity Identity { + get { + return _identity; + } + set { + _identity = value; + } + } + + public MenuIdentityItem() { + InitializeComponent(); + ToggleSwitch.OnToggled += ToggleIdentity; + } + + async private void ToggleIdentity(bool on) { + try { + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + DataStructures.Identity id = await client.IdentityOnOffAsync(_identity.Identifier, on); + this.Identity.IsEnabled = on; + } catch (DataStructures.ServiceException se) { + MessageBox.Show(se.AdditionalInfo, se.Message); + } catch (Exception ex) { + MessageBox.Show("Error", ex.Message); + } + } + + private void ShowIdentity(object sender, MouseButtonEventArgs e) { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + deets.SelectedIdentityMenu = this; + deets.Identity = this.Identity; + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/MenuItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/MenuItem.xaml.cs index dd4da3c37..dbdfc1c7e 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/MenuItem.xaml.cs @@ -1,56 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MenuItem.xaml - /// - public partial class MenuItem : UserControl { - - private string _label = ""; - private string _icon = ""; - - public string Label { - get { - return _label; - } - set { - this._label = value; - MainLabel.Content = this._label; - } - } - public string Icon { - get { - return _icon; - } - set { - this._icon = value; - MainSource.Source = new BitmapImage(new Uri(this._icon, UriKind.Relative)); - } - } - - public MenuItem() { - InitializeComponent(); - } - - private void UserControl_MouseEnter(object sender, MouseEventArgs e) { - - } - - private void UserControl_MouseLeave(object sender, MouseEventArgs e) { - - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MenuItem.xaml + /// + public partial class MenuItem : UserControl { + + private string _label = ""; + private string _icon = ""; + + public string Label { + get { + return _label; + } + set { + this._label = value; + MainLabel.Content = this._label; + } + } + public string Icon { + get { + return _icon; + } + set { + this._icon = value; + MainSource.Source = new BitmapImage(new Uri(this._icon, UriKind.Relative)); + } + } + + public MenuItem() { + InitializeComponent(); + } + + private void UserControl_MouseEnter(object sender, MouseEventArgs e) { + + } + + private void UserControl_MouseLeave(object sender, MouseEventArgs e) { + + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/SubMenuItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/SubMenuItem.xaml.cs index 5c9de79e5..a9c1f3794 100644 --- a/DesktopEdge/Views/ItemRenderers/SubMenuItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/SubMenuItem.xaml.cs @@ -1,38 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MenuItem.xaml - /// - public partial class SubMenuItem : UserControl { - - private string _label = ""; - - public string Label { - get { - return _label; - } - set { - this._label = value; - MainLabel.Content = this._label; - } - } - - public SubMenuItem() { - InitializeComponent(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MenuItem.xaml + /// + public partial class SubMenuItem : UserControl { + + private string _label = ""; + + public string Label { + get { + return _label; + } + set { + this._label = value; + MainLabel.Content = this._label; + } + } + + public SubMenuItem() { + InitializeComponent(); + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/SubOptionItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/SubOptionItem.xaml.cs index 8a4467e82..b567804e9 100644 --- a/DesktopEdge/Views/ItemRenderers/SubOptionItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/SubOptionItem.xaml.cs @@ -1,50 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MenuItem.xaml - /// - public partial class SubOptionItem : UserControl { - - private string _label = ""; - private bool _isSelected = false; - - public string Label { - get { - return _label; - } - set { - this._label = value; - MainLabel.Content = this._label; - } - } - - public bool IsSelected { - get { - return _isSelected; - } - set { - this._isSelected = value; - if (this._isSelected) SelectedCheck.Visibility = Visibility.Visible; - else SelectedCheck.Visibility = Visibility.Collapsed; - } - } - - public SubOptionItem() { - InitializeComponent(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MenuItem.xaml + /// + public partial class SubOptionItem : UserControl { + + private string _label = ""; + private bool _isSelected = false; + + public string Label { + get { + return _label; + } + set { + this._label = value; + MainLabel.Content = this._label; + } + } + + public bool IsSelected { + get { + return _isSelected; + } + set { + this._isSelected = value; + if (this._isSelected) SelectedCheck.Visibility = Visibility.Visible; + else SelectedCheck.Visibility = Visibility.Collapsed; + } + } + + public SubOptionItem() { + InitializeComponent(); + } + } +} diff --git a/DesktopEdge/Views/Screens/Debugging.xaml.cs b/DesktopEdge/Views/Screens/Debugging.xaml.cs index fcab8199c..8076e5911 100644 --- a/DesktopEdge/Views/Screens/Debugging.xaml.cs +++ b/DesktopEdge/Views/Screens/Debugging.xaml.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs b/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs index ba4336e54..e306171e4 100644 --- a/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs +++ b/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -32,529 +32,529 @@ limitations under the License. using System.Diagnostics.Eventing.Reader; namespace ZitiDesktopEdge { - /// - /// Interaction logic for IdentityDetails.xaml - /// - public partial class IdentityDetails:UserControl { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - private bool _isAttached = true; - public delegate void Forgot(ZitiIdentity forgotten); - public event Forgot OnForgot; - public delegate void ErrorOccurred(string message); - public event ErrorOccurred OnError; - public delegate void MFAToggled(bool isOn); - public event MFAToggled OnMFAToggled; - public delegate void Detched(MouseButtonEventArgs e); - public event Detched OnDetach; - public delegate void Mesage(string message); - public event Mesage OnMessage; - public delegate void OnAuthenticate(ZitiIdentity identity); - public event OnAuthenticate Authenticate; - public delegate void OnRecovery(ZitiIdentity identity); - public event OnRecovery Recovery; - public delegate void LoadingEvent(bool isComplete); - public event LoadingEvent OnLoading; - public delegate void ShowMFAEvent(ZitiIdentity identity); - public event ShowMFAEvent OnShowMFA; - - private System.Windows.Forms.Timer _timer; - public double MainHeight = 500; - public string filter = ""; - public int Page = 1; - public int PerPage = 50; - public int TotalPages = 1; - public string SortBy = "Name"; - public string SortWay = "Asc"; - private bool _loaded = false; - private double scrolledTo = 0; - public int totalServices = 0; - private ScrollViewer _scroller; - private ZitiService _info; - - public ObservableCollection _services = new ObservableCollection(); - public ObservableCollection ZitiServices { get { return _services; } } - - internal MainWindow MainWindow { get; set; } - - private List identities { - get { - return (List)Application.Current.Properties["Identities"]; - } - } - - private ZitiIdentity _identity; - - public ZitiIdentity Identity { - get { - return _identity; - } - set { - _loaded = false; - FilterServices.Clear(); - scrolledTo = 0; - _identity = value; - ServiceCount.Content = _identity.Services.Count + " service"+((_identity.Services.Count!=1)?"s":""); - Page = 1; - SortBy = "Name"; - SortWay = "Asc"; - filter = ""; - UpdateView(); - IdentityArea.Opacity = 1.0; - IdentityArea.Visibility = Visibility.Visible; - this.Visibility = Visibility.Visible; - } - } - - public IdentityItem SelectedIdentity { get; set; } - public MenuIdentityItem SelectedIdentityMenu { get; set; } - - private void Window_MouseDown(object sender, MouseButtonEventArgs e) { - if (e.ChangedButton == MouseButton.Left) { - _isAttached = false; - OnDetach(e); - } - } - - - public bool IsAttached { - get { - return _isAttached; - } - set { - _isAttached = value; - if (_isAttached) { - Arrow.Visibility = Visibility.Visible; - ConfirmArrow.Visibility = Visibility.Visible; - } else { - Arrow.Visibility = Visibility.Collapsed; - ConfirmArrow.Visibility = Visibility.Collapsed; - } - } - } - - - private void MFAEnabledAndNeeded() { - if (_identity.Services.Count > 0) { - MainDetailScroll.Visibility = Visibility.Visible; - } else { - IdentityMFA.AuthOff.Visibility = Visibility.Visible; - AuthMessageBg.Visibility = Visibility.Visible; - AuthMessageLabel.Visibility = Visibility.Visible; - NoAuthServices.Visibility = Visibility.Visible; - NoAuthServices.Text = "You must authenticate to access services"; - } - } - - private void MFAEnabledAndNotNeeded() { - MainDetailScroll.Visibility = Visibility.Visible; - IdentityMFA.AuthOn.Visibility = Visibility.Visible; - } - - private void MFANotEnabledAndNotNeeded() { - MainDetailScroll.Visibility = Visibility.Visible; - } - - private void MFANotEnabledAndNeeded() { - if (_identity.Services.Count > 0) { - MainDetailScroll.Visibility = Visibility.Visible; - } else { - AuthMessageLabel.Visibility = Visibility.Visible; - NoAuthServices.Visibility = Visibility.Visible; - NoAuthServices.Text = "You must enable MFA to access services"; - ServiceCount.Visibility = Visibility.Collapsed; - } - } - - public void UpdateView() { - if (_scroller==null) { - _scroller = GetScrollViewer(ServiceList) as ScrollViewer; - } - if (_scroller != null) { - _scroller.InvalidateScrollInfo(); - _scroller.ScrollToVerticalOffset(0); - _scroller.InvalidateScrollInfo(); - } - - IsConnected.Visibility = Visibility.Collapsed; - IsDisconnected.Visibility = Visibility.Collapsed; - IsConnected.Visibility = Visibility.Collapsed; - IsDisconnected.Visibility = Visibility.Collapsed; - MainDetailScroll.Visibility = Visibility.Collapsed; - AuthMessageBg.Visibility = Visibility.Collapsed; - AuthMessageLabel.Visibility = Visibility.Collapsed; - NoAuthServices.Visibility = Visibility.Collapsed; - IdentityMFA.AuthOn.Visibility = Visibility.Collapsed; - IdentityMFA.AuthOff.Visibility = Visibility.Collapsed; - IdentityMFA.RecoveryButton.Visibility = Visibility.Collapsed; - ServiceCount.Visibility = Visibility.Visible; - - scrolledTo = 0; - IdDetailName.Text = _identity.Name; - IdDetailName.ToolTip = _identity.Name; - IdentityMFA.IsOn = _identity.IsMFAEnabled; - IdentityMFA.ToggleField.IsEnabled = true; - IdentityMFA.ToggleField.Opacity = 1; - IsConnected.ToolTip = "Enabled - Click To Disable"; - IsDisconnected.ToolTip = "Disabled - Click to Enable"; - IdServer.Value = _identity.ControllerUrl; - IdName.Value = _identity.Name; + /// + /// Interaction logic for IdentityDetails.xaml + /// + public partial class IdentityDetails : UserControl { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private bool _isAttached = true; + public delegate void Forgot(ZitiIdentity forgotten); + public event Forgot OnForgot; + public delegate void ErrorOccurred(string message); + public event ErrorOccurred OnError; + public delegate void MFAToggled(bool isOn); + public event MFAToggled OnMFAToggled; + public delegate void Detched(MouseButtonEventArgs e); + public event Detched OnDetach; + public delegate void Mesage(string message); + public event Mesage OnMessage; + public delegate void OnAuthenticate(ZitiIdentity identity); + public event OnAuthenticate Authenticate; + public delegate void OnRecovery(ZitiIdentity identity); + public event OnRecovery Recovery; + public delegate void LoadingEvent(bool isComplete); + public event LoadingEvent OnLoading; + public delegate void ShowMFAEvent(ZitiIdentity identity); + public event ShowMFAEvent OnShowMFA; + + private System.Windows.Forms.Timer _timer; + public double MainHeight = 500; + public string filter = ""; + public int Page = 1; + public int PerPage = 50; + public int TotalPages = 1; + public string SortBy = "Name"; + public string SortWay = "Asc"; + private bool _loaded = false; + private double scrolledTo = 0; + public int totalServices = 0; + private ScrollViewer _scroller; + private ZitiService _info; + + public ObservableCollection _services = new ObservableCollection(); + public ObservableCollection ZitiServices { get { return _services; } } + + internal MainWindow MainWindow { get; set; } + + private List identities { + get { + return (List)Application.Current.Properties["Identities"]; + } + } + + private ZitiIdentity _identity; + + public ZitiIdentity Identity { + get { + return _identity; + } + set { + _loaded = false; + FilterServices.Clear(); + scrolledTo = 0; + _identity = value; + ServiceCount.Content = _identity.Services.Count + " service" + ((_identity.Services.Count != 1) ? "s" : ""); + Page = 1; + SortBy = "Name"; + SortWay = "Asc"; + filter = ""; + UpdateView(); + IdentityArea.Opacity = 1.0; + IdentityArea.Visibility = Visibility.Visible; + this.Visibility = Visibility.Visible; + } + } + + public IdentityItem SelectedIdentity { get; set; } + public MenuIdentityItem SelectedIdentityMenu { get; set; } + + private void Window_MouseDown(object sender, MouseButtonEventArgs e) { + if (e.ChangedButton == MouseButton.Left) { + _isAttached = false; + OnDetach(e); + } + } + + + public bool IsAttached { + get { + return _isAttached; + } + set { + _isAttached = value; + if (_isAttached) { + Arrow.Visibility = Visibility.Visible; + ConfirmArrow.Visibility = Visibility.Visible; + } else { + Arrow.Visibility = Visibility.Collapsed; + ConfirmArrow.Visibility = Visibility.Collapsed; + } + } + } + + + private void MFAEnabledAndNeeded() { + if (_identity.Services.Count > 0) { + MainDetailScroll.Visibility = Visibility.Visible; + } else { + IdentityMFA.AuthOff.Visibility = Visibility.Visible; + AuthMessageBg.Visibility = Visibility.Visible; + AuthMessageLabel.Visibility = Visibility.Visible; + NoAuthServices.Visibility = Visibility.Visible; + NoAuthServices.Text = "You must authenticate to access services"; + } + } + + private void MFAEnabledAndNotNeeded() { + MainDetailScroll.Visibility = Visibility.Visible; + IdentityMFA.AuthOn.Visibility = Visibility.Visible; + } + + private void MFANotEnabledAndNotNeeded() { + MainDetailScroll.Visibility = Visibility.Visible; + } + + private void MFANotEnabledAndNeeded() { + if (_identity.Services.Count > 0) { + MainDetailScroll.Visibility = Visibility.Visible; + } else { + AuthMessageLabel.Visibility = Visibility.Visible; + NoAuthServices.Visibility = Visibility.Visible; + NoAuthServices.Text = "You must enable MFA to access services"; + ServiceCount.Visibility = Visibility.Collapsed; + } + } + + public void UpdateView() { + if (_scroller == null) { + _scroller = GetScrollViewer(ServiceList) as ScrollViewer; + } + if (_scroller != null) { + _scroller.InvalidateScrollInfo(); + _scroller.ScrollToVerticalOffset(0); + _scroller.InvalidateScrollInfo(); + } + + IsConnected.Visibility = Visibility.Collapsed; + IsDisconnected.Visibility = Visibility.Collapsed; + IsConnected.Visibility = Visibility.Collapsed; + IsDisconnected.Visibility = Visibility.Collapsed; + MainDetailScroll.Visibility = Visibility.Collapsed; + AuthMessageBg.Visibility = Visibility.Collapsed; + AuthMessageLabel.Visibility = Visibility.Collapsed; + NoAuthServices.Visibility = Visibility.Collapsed; + IdentityMFA.AuthOn.Visibility = Visibility.Collapsed; + IdentityMFA.AuthOff.Visibility = Visibility.Collapsed; + IdentityMFA.RecoveryButton.Visibility = Visibility.Collapsed; + ServiceCount.Visibility = Visibility.Visible; + + scrolledTo = 0; + IdDetailName.Text = _identity.Name; + IdDetailName.ToolTip = _identity.Name; + IdentityMFA.IsOn = _identity.IsMFAEnabled; + IdentityMFA.ToggleField.IsEnabled = true; + IdentityMFA.ToggleField.Opacity = 1; + IsConnected.ToolTip = "Enabled - Click To Disable"; + IsDisconnected.ToolTip = "Disabled - Click to Enable"; + IdServer.Value = _identity.ControllerUrl; + IdName.Value = _identity.Name; #if DEBUG - _identity.MFADebug("UpdateView"); + _identity.MFADebug("UpdateView"); #endif - if (_identity.IsMFAEnabled) { - if (_identity.IsMFANeeded) { - // enabled and needed = needs to be authorized. show the lock icon and tell the user to auth - MFAEnabledAndNeeded(); - } else { - // enabled and not needed = authorized. show the services should be enabled and authorized - MFAEnabledAndNotNeeded(); - } - } else { - if (_identity.IsMFANeeded) { - // not enabled and needed = show the user the MFA disabled so they can enable it - MFANotEnabledAndNeeded(); - } else { - // normal case. means no lock icon needs to be shown - MFANotEnabledAndNotNeeded(); - } - } - - //change icon to timed out if it's timed out - - - totalServices = _identity.Services.Count; - - if (_identity.Services.Count>0) { - int index = 0; - int total = 0; - ZitiService[] services = new ZitiService[0]; - _services = null; - _services = new ObservableCollection(); - if (SortBy == "Name") services = _identity.Services.OrderBy(s => s.Name.ToLower()).ToArray(); - else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray(); - else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray(); - else if (SortBy == "Port") services = _identity.Services.OrderBy(s => s.Ports.ToString()).ToArray(); - if (SortWay == "Desc") services = services.Reverse().ToArray(); - int startIndex = (Page - 1) * PerPage; - for (int i = startIndex; i < services.Length; i++) { - ZitiService zitiSvc = services[i]; - total++; - if (index < PerPage) { - if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) { - zitiSvc.TimeUpdated = _identity.LastUpdatedTime; - zitiSvc.IsMfaReady = _identity.IsMFAEnabled; - _services.Add(zitiSvc); - index++; - } - } - } - ServiceList.ItemsSource = ZitiServices; - - TotalPages = (total / PerPage) + 1; - - double newHeight = MainHeight - 330; - MainDetailScroll.MaxHeight = newHeight; - MainDetailScroll.Height = newHeight; - - - } - ConfirmView.Visibility = Visibility.Collapsed; - _loaded = true; - } - - private void ShowDetails(ZitiService info) { - _info = info; - DetailName.Text = info.Name; - DetailProtocols.Text = info.ProtocolString; - DetailAddress.Text = info.AddressString; - DetailPorts.Text = info.PortString; - DetailUrl.Text = info.ToString(); - - UpdateClock(info); - if (_identity.IsMFAEnabled) { - if (_timer != null) _timer.Stop(); - _timer = new System.Windows.Forms.Timer(); - _timer.Interval = 1000; - _timer.Tick += Ticked; ; - _timer.Start(); - } - - DetailsArea.Visibility = Visibility.Visible; - DetailsArea.Opacity = 0; - DetailsArea.Margin = new Thickness(0, 0, 0, 0); - DoubleAnimation animation = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); - animation.Completed += ShowCompleted; - DetailsArea.BeginAnimation(Grid.OpacityProperty, animation); - DetailsArea.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - - ShowModal(); - } - - private void Ticked(object sender, EventArgs e) { - UpdateClock(_info); - } - - private void UpdateClock(ZitiService info) { - try { - if (_identity.IsMFAEnabled) { - if (info.TimeoutCalculated > 0) { - TimeSpan t = TimeSpan.FromSeconds(info.TimeoutCalculated); - string answer = t.Seconds + " seconds"; - if (t.Days > 0) answer = t.Days + " days " + t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; - else { - if (t.Hours > 0) answer = t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; - else { - if (t.Minutes > 0) answer = t.Minutes + " minutes " + t.Seconds + " seconds"; - } - } - TimeoutDetails.Text = answer; - } else { - if (info.TimeoutCalculated == 0) TimeoutDetails.Text = "Timed Out"; - else TimeoutDetails.Text = "Never"; - } - } else { - TimeoutDetails.Text = "Never"; - } - } catch (Exception e) { - TimeoutDetails.Text = "Never"; - Console.WriteLine("Error: " + e.ToString()); - } - } - - private void ShowCompleted(object sender, EventArgs e) { - DoubleAnimation animation = new DoubleAnimation(DetailPanel.ActualHeight + 60, TimeSpan.FromSeconds(.3)); - DetailsArea.BeginAnimation(Grid.HeightProperty, animation); - //DetailsArea.Height = DetailPanel.ActualHeight + 60; - } - - private void CloseDetails(object sender, MouseButtonEventArgs e) { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); - DoubleAnimation animation2 = new DoubleAnimation(DetailPanel.ActualHeight + 100, TimeSpan.FromSeconds(.3)); - animation.Completed += HideComplete; - DetailsArea.BeginAnimation(Grid.HeightProperty, animation2); - DetailsArea.BeginAnimation(Grid.OpacityProperty, animation); - DetailsArea.BeginAnimation(Grid.MarginProperty, animateThick); - HideModal(); - } - - private void HideComplete(object sender, EventArgs e) { - DetailsArea.Visibility = Visibility.Collapsed; - ModalBg.Visibility = Visibility.Collapsed; - } - - private void Info_OnMessage(string message) { - OnMessage?.Invoke(message); - } - - public IdentityDetails() { - InitializeComponent(); - } - private void HideMenu(object sender, MouseButtonEventArgs e) { - this.Visibility = Visibility.Collapsed; - } - - public void SetHeight(double height) { - MainDetailScroll.Height = height; - } - - private void ForgetIdentity(object sender, MouseButtonEventArgs e) { - if (this.Visibility==Visibility.Visible&&ConfirmView.Visibility==Visibility.Collapsed) { - ConfirmView.Visibility = Visibility.Visible; - } - } - - private void CancelConfirmButton_Click(object sender, RoutedEventArgs e) { - ConfirmView.Visibility = Visibility.Collapsed; - } - - async private void ConfirmButton_Click(object sender, RoutedEventArgs e) { - this.Visibility = Visibility.Collapsed; - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - try { - ConfirmView.Visibility = Visibility.Collapsed; - await client.RemoveIdentityAsync(_identity.Identifier); - - ZitiIdentity forgotten = new ZitiIdentity(); - foreach (var id in identities) { - if (id.Identifier == _identity.Identifier) { - forgotten = id; - identities.Remove(id); - break; - } - } - - OnForgot?.Invoke(forgotten); - } catch (DataStructures.ServiceException se) { - Logger.Error(se, se.Message); - OnError(se.Message); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected: "+ ex.Message); - OnError("An unexpected error has occured while removing the identity. Please verify the service is still running and try again."); - } - } - - private void ToggleMFA(bool isOn) { - this.OnMFAToggled?.Invoke(isOn); - } - - /* Modal UI Background visibility */ - - /// - /// Show the modal, aniimating opacity - /// - private void ShowModal() { - ModalBg.Visibility = Visibility.Visible; - ModalBg.Opacity = 0; - ModalBg.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(.8, TimeSpan.FromSeconds(.3))); - } - - /// - /// Hide the modal animating the opacity - /// - private void HideModal() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - animation.Completed += ModalHideComplete; - ModalBg.BeginAnimation(Grid.OpacityProperty, animation); - } - - /// - /// When the animation completes, set the visibility to avoid UI object conflicts - /// - /// The animation - /// The event - private void ModalHideComplete(object sender, EventArgs e) { - ModalBg.Visibility = Visibility.Collapsed; - } - - private void MFARecovery() { - this.Recovery?.Invoke(this.Identity); - } - - private void MFAAuthenticate() { - this.Authenticate.Invoke(this.Identity); - } - - private void AuthFromMessage(object sender, MouseButtonEventArgs e) { - this.Authenticate.Invoke(this.Identity); - } - - public static DependencyObject GetScrollViewer(DependencyObject o) { - if (o is ScrollViewer) { return o; } - for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) { - var child = VisualTreeHelper.GetChild(o, i); - var result = GetScrollViewer(child); - if (result == null) { - continue; - } else { - return result; - } - } - return null; - } - - private void Scrolled(object sender, ScrollChangedEventArgs e) { - if (_loaded) { - if (_scroller == null) { - _scroller = GetScrollViewer(ServiceList) as ScrollViewer; - } - var verticalOffset = _scroller.VerticalOffset; - var maxVerticalOffset = _scroller.ScrollableHeight; - - if ((maxVerticalOffset < 0 || verticalOffset == maxVerticalOffset) && verticalOffset>0 && scrolledTo s.Name.ToLower()).ToArray(); - else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray(); - else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray(); - else if (SortBy == "Port") services = _identity.Services.OrderBy(s => s.Ports.ToString()).ToArray(); - if (SortWay == "Desc") services = services.Reverse().ToArray(); - int startIndex = (Page - 1) * PerPage; - for (int i = startIndex; i < services.Length; i++) { - ZitiService zitiSvc = services[i]; - if (index < PerPage) { - if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) { - zitiSvc.TimeUpdated = _identity.LastUpdatedTime; - ZitiServices.Add(zitiSvc); - index++; - } - } - } - double totalOffset = _scroller.VerticalOffset; - double toNegate = index * 33; - double scrollTo = (totalOffset - toNegate); - _scroller.InvalidateScrollInfo(); - _scroller.ScrollToVerticalOffset(verticalOffset); - _scroller.InvalidateScrollInfo(); - _loaded = true; - // scrollViewer.ScrollChanged += Scrolled; - } - } - } - } - - private void DoFilter(FilterData filterData) { - filter = filterData.SearchFor; - SortBy = filterData.SortBy; - SortWay = filterData.SortHow; - UpdateView(); - } - - async private void DoConnect(object sender, MouseButtonEventArgs e) { - this.OnLoading?.Invoke(false); - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - await client.IdentityOnOffAsync(_identity.Identifier, true); - if (SelectedIdentity != null) SelectedIdentity.ToggleSwitch.Enabled = true; - if (SelectedIdentityMenu != null) SelectedIdentityMenu.ToggleSwitch.Enabled = true; - _identity.IsEnabled = true; - IsConnected.Visibility = Visibility.Visible; - IsDisconnected.Visibility = Visibility.Collapsed; - this.OnLoading?.Invoke(true); - } - - async private void DoDisconnect(object sender, MouseButtonEventArgs e) { - this.OnLoading?.Invoke(false); - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - await client.IdentityOnOffAsync(_identity.Identifier, false); - if (SelectedIdentity != null) SelectedIdentity.ToggleSwitch.Enabled = false; - if (SelectedIdentityMenu != null) SelectedIdentityMenu.ToggleSwitch.Enabled = false; - _identity.IsEnabled = false; - IsConnected.Visibility = Visibility.Collapsed; - IsDisconnected.Visibility = Visibility.Visible; - this.OnLoading?.Invoke(true); - } - - private void WarnClicked(object sender, MouseButtonEventArgs e) { - ZitiService item = (ZitiService)(sender as FrameworkElement).DataContext; - OnMessage?.Invoke(item.WarningMessage); - } - - private void DetailsClicked(object sender, MouseButtonEventArgs e) { - ZitiService item = (ZitiService)(sender as FrameworkElement).DataContext; - ShowDetails(item); - } - - private void VisibilityChanged(object sender, DependencyPropertyChangedEventArgs e) { - if (this.Visibility==Visibility.Collapsed) { - ServiceList.DataContext = null; - if (_services!=null) { - _services.Clear(); - _services = null; - } - } - } - - private void DoMFA(object sender, MouseButtonEventArgs e) { - this.OnShowMFA?.Invoke(this._identity); - } - } + if (_identity.IsMFAEnabled) { + if (_identity.IsMFANeeded) { + // enabled and needed = needs to be authorized. show the lock icon and tell the user to auth + MFAEnabledAndNeeded(); + } else { + // enabled and not needed = authorized. show the services should be enabled and authorized + MFAEnabledAndNotNeeded(); + } + } else { + if (_identity.IsMFANeeded) { + // not enabled and needed = show the user the MFA disabled so they can enable it + MFANotEnabledAndNeeded(); + } else { + // normal case. means no lock icon needs to be shown + MFANotEnabledAndNotNeeded(); + } + } + + //change icon to timed out if it's timed out + + + totalServices = _identity.Services.Count; + + if (_identity.Services.Count > 0) { + int index = 0; + int total = 0; + ZitiService[] services = new ZitiService[0]; + _services = null; + _services = new ObservableCollection(); + if (SortBy == "Name") services = _identity.Services.OrderBy(s => s.Name.ToLower()).ToArray(); + else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray(); + else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray(); + else if (SortBy == "Port") services = _identity.Services.OrderBy(s => s.Ports.ToString()).ToArray(); + if (SortWay == "Desc") services = services.Reverse().ToArray(); + int startIndex = (Page - 1) * PerPage; + for (int i = startIndex; i < services.Length; i++) { + ZitiService zitiSvc = services[i]; + total++; + if (index < PerPage) { + if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) { + zitiSvc.TimeUpdated = _identity.LastUpdatedTime; + zitiSvc.IsMfaReady = _identity.IsMFAEnabled; + _services.Add(zitiSvc); + index++; + } + } + } + ServiceList.ItemsSource = ZitiServices; + + TotalPages = (total / PerPage) + 1; + + double newHeight = MainHeight - 330; + MainDetailScroll.MaxHeight = newHeight; + MainDetailScroll.Height = newHeight; + + + } + ConfirmView.Visibility = Visibility.Collapsed; + _loaded = true; + } + + private void ShowDetails(ZitiService info) { + _info = info; + DetailName.Text = info.Name; + DetailProtocols.Text = info.ProtocolString; + DetailAddress.Text = info.AddressString; + DetailPorts.Text = info.PortString; + DetailUrl.Text = info.ToString(); + + UpdateClock(info); + if (_identity.IsMFAEnabled) { + if (_timer != null) _timer.Stop(); + _timer = new System.Windows.Forms.Timer(); + _timer.Interval = 1000; + _timer.Tick += Ticked; ; + _timer.Start(); + } + + DetailsArea.Visibility = Visibility.Visible; + DetailsArea.Opacity = 0; + DetailsArea.Margin = new Thickness(0, 0, 0, 0); + DoubleAnimation animation = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); + animation.Completed += ShowCompleted; + DetailsArea.BeginAnimation(Grid.OpacityProperty, animation); + DetailsArea.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + + ShowModal(); + } + + private void Ticked(object sender, EventArgs e) { + UpdateClock(_info); + } + + private void UpdateClock(ZitiService info) { + try { + if (_identity.IsMFAEnabled) { + if (info.TimeoutCalculated > 0) { + TimeSpan t = TimeSpan.FromSeconds(info.TimeoutCalculated); + string answer = t.Seconds + " seconds"; + if (t.Days > 0) answer = t.Days + " days " + t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; + else { + if (t.Hours > 0) answer = t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; + else { + if (t.Minutes > 0) answer = t.Minutes + " minutes " + t.Seconds + " seconds"; + } + } + TimeoutDetails.Text = answer; + } else { + if (info.TimeoutCalculated == 0) TimeoutDetails.Text = "Timed Out"; + else TimeoutDetails.Text = "Never"; + } + } else { + TimeoutDetails.Text = "Never"; + } + } catch (Exception e) { + TimeoutDetails.Text = "Never"; + Console.WriteLine("Error: " + e.ToString()); + } + } + + private void ShowCompleted(object sender, EventArgs e) { + DoubleAnimation animation = new DoubleAnimation(DetailPanel.ActualHeight + 60, TimeSpan.FromSeconds(.3)); + DetailsArea.BeginAnimation(Grid.HeightProperty, animation); + //DetailsArea.Height = DetailPanel.ActualHeight + 60; + } + + private void CloseDetails(object sender, MouseButtonEventArgs e) { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + DoubleAnimation animation2 = new DoubleAnimation(DetailPanel.ActualHeight + 100, TimeSpan.FromSeconds(.3)); + animation.Completed += HideComplete; + DetailsArea.BeginAnimation(Grid.HeightProperty, animation2); + DetailsArea.BeginAnimation(Grid.OpacityProperty, animation); + DetailsArea.BeginAnimation(Grid.MarginProperty, animateThick); + HideModal(); + } + + private void HideComplete(object sender, EventArgs e) { + DetailsArea.Visibility = Visibility.Collapsed; + ModalBg.Visibility = Visibility.Collapsed; + } + + private void Info_OnMessage(string message) { + OnMessage?.Invoke(message); + } + + public IdentityDetails() { + InitializeComponent(); + } + private void HideMenu(object sender, MouseButtonEventArgs e) { + this.Visibility = Visibility.Collapsed; + } + + public void SetHeight(double height) { + MainDetailScroll.Height = height; + } + + private void ForgetIdentity(object sender, MouseButtonEventArgs e) { + if (this.Visibility == Visibility.Visible && ConfirmView.Visibility == Visibility.Collapsed) { + ConfirmView.Visibility = Visibility.Visible; + } + } + + private void CancelConfirmButton_Click(object sender, RoutedEventArgs e) { + ConfirmView.Visibility = Visibility.Collapsed; + } + + async private void ConfirmButton_Click(object sender, RoutedEventArgs e) { + this.Visibility = Visibility.Collapsed; + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + try { + ConfirmView.Visibility = Visibility.Collapsed; + await client.RemoveIdentityAsync(_identity.Identifier); + + ZitiIdentity forgotten = new ZitiIdentity(); + foreach (var id in identities) { + if (id.Identifier == _identity.Identifier) { + forgotten = id; + identities.Remove(id); + break; + } + } + + OnForgot?.Invoke(forgotten); + } catch (DataStructures.ServiceException se) { + Logger.Error(se, se.Message); + OnError(se.Message); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected: " + ex.Message); + OnError("An unexpected error has occured while removing the identity. Please verify the service is still running and try again."); + } + } + + private void ToggleMFA(bool isOn) { + this.OnMFAToggled?.Invoke(isOn); + } + + /* Modal UI Background visibility */ + + /// + /// Show the modal, aniimating opacity + /// + private void ShowModal() { + ModalBg.Visibility = Visibility.Visible; + ModalBg.Opacity = 0; + ModalBg.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(.8, TimeSpan.FromSeconds(.3))); + } + + /// + /// Hide the modal animating the opacity + /// + private void HideModal() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + animation.Completed += ModalHideComplete; + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// When the animation completes, set the visibility to avoid UI object conflicts + /// + /// The animation + /// The event + private void ModalHideComplete(object sender, EventArgs e) { + ModalBg.Visibility = Visibility.Collapsed; + } + + private void MFARecovery() { + this.Recovery?.Invoke(this.Identity); + } + + private void MFAAuthenticate() { + this.Authenticate.Invoke(this.Identity); + } + + private void AuthFromMessage(object sender, MouseButtonEventArgs e) { + this.Authenticate.Invoke(this.Identity); + } + + public static DependencyObject GetScrollViewer(DependencyObject o) { + if (o is ScrollViewer) { return o; } + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) { + var child = VisualTreeHelper.GetChild(o, i); + var result = GetScrollViewer(child); + if (result == null) { + continue; + } else { + return result; + } + } + return null; + } + + private void Scrolled(object sender, ScrollChangedEventArgs e) { + if (_loaded) { + if (_scroller == null) { + _scroller = GetScrollViewer(ServiceList) as ScrollViewer; + } + var verticalOffset = _scroller.VerticalOffset; + var maxVerticalOffset = _scroller.ScrollableHeight; + + if ((maxVerticalOffset < 0 || verticalOffset == maxVerticalOffset) && verticalOffset > 0 && scrolledTo < verticalOffset) { + if (Page < TotalPages) { + scrolledTo = verticalOffset; + // scrollViewer.ScrollChanged -= Scrolled; + Logger.Trace("Paging: " + Page); + _loaded = false; + Page += 1; + int index = 0; + ZitiService[] services = new ZitiService[0]; + if (SortBy == "Name") services = _identity.Services.OrderBy(s => s.Name.ToLower()).ToArray(); + else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray(); + else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray(); + else if (SortBy == "Port") services = _identity.Services.OrderBy(s => s.Ports.ToString()).ToArray(); + if (SortWay == "Desc") services = services.Reverse().ToArray(); + int startIndex = (Page - 1) * PerPage; + for (int i = startIndex; i < services.Length; i++) { + ZitiService zitiSvc = services[i]; + if (index < PerPage) { + if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) { + zitiSvc.TimeUpdated = _identity.LastUpdatedTime; + ZitiServices.Add(zitiSvc); + index++; + } + } + } + double totalOffset = _scroller.VerticalOffset; + double toNegate = index * 33; + double scrollTo = (totalOffset - toNegate); + _scroller.InvalidateScrollInfo(); + _scroller.ScrollToVerticalOffset(verticalOffset); + _scroller.InvalidateScrollInfo(); + _loaded = true; + // scrollViewer.ScrollChanged += Scrolled; + } + } + } + } + + private void DoFilter(FilterData filterData) { + filter = filterData.SearchFor; + SortBy = filterData.SortBy; + SortWay = filterData.SortHow; + UpdateView(); + } + + async private void DoConnect(object sender, MouseButtonEventArgs e) { + this.OnLoading?.Invoke(false); + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + await client.IdentityOnOffAsync(_identity.Identifier, true); + if (SelectedIdentity != null) SelectedIdentity.ToggleSwitch.Enabled = true; + if (SelectedIdentityMenu != null) SelectedIdentityMenu.ToggleSwitch.Enabled = true; + _identity.IsEnabled = true; + IsConnected.Visibility = Visibility.Visible; + IsDisconnected.Visibility = Visibility.Collapsed; + this.OnLoading?.Invoke(true); + } + + async private void DoDisconnect(object sender, MouseButtonEventArgs e) { + this.OnLoading?.Invoke(false); + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + await client.IdentityOnOffAsync(_identity.Identifier, false); + if (SelectedIdentity != null) SelectedIdentity.ToggleSwitch.Enabled = false; + if (SelectedIdentityMenu != null) SelectedIdentityMenu.ToggleSwitch.Enabled = false; + _identity.IsEnabled = false; + IsConnected.Visibility = Visibility.Collapsed; + IsDisconnected.Visibility = Visibility.Visible; + this.OnLoading?.Invoke(true); + } + + private void WarnClicked(object sender, MouseButtonEventArgs e) { + ZitiService item = (ZitiService)(sender as FrameworkElement).DataContext; + OnMessage?.Invoke(item.WarningMessage); + } + + private void DetailsClicked(object sender, MouseButtonEventArgs e) { + ZitiService item = (ZitiService)(sender as FrameworkElement).DataContext; + ShowDetails(item); + } + + private void VisibilityChanged(object sender, DependencyPropertyChangedEventArgs e) { + if (this.Visibility == Visibility.Collapsed) { + ServiceList.DataContext = null; + if (_services != null) { + _services.Clear(); + _services = null; + } + } + } + + private void DoMFA(object sender, MouseButtonEventArgs e) { + this.OnShowMFA?.Invoke(this._identity); + } + } } \ No newline at end of file diff --git a/DesktopEdge/Views/Screens/MFAScreen.xaml.cs b/DesktopEdge/Views/Screens/MFAScreen.xaml.cs index 3015ebbcb..57257a557 100644 --- a/DesktopEdge/Views/Screens/MFAScreen.xaml.cs +++ b/DesktopEdge/Views/Screens/MFAScreen.xaml.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -41,304 +41,304 @@ limitations under the License. using System.Text.RegularExpressions; namespace ZitiDesktopEdge { - /// - /// Interaction logic for MFA.xaml - /// - public partial class MFAScreen : UserControl { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public delegate void LoadEvent(bool isComplete, string title, string message); - public event LoadEvent OnLoad; - public delegate void CloseAction(bool isComplete); - public event CloseAction OnClose; - private string _url = ""; - public delegate void ErrorOccurred(string message); - public event ErrorOccurred OnError; - private string[] _codes = new string[0]; - private ZitiIdentity zid; - private bool _executing = false; - public int Type { get; set; } - - public ZitiIdentity Identity { - get { - return this.zid; - } - set { - this.zid = value; - } - } - - public MFAScreen() { - InitializeComponent(); - } - - private void ExecuteClose(object sender, MouseButtonEventArgs e) { - this.OnClose?.Invoke(false); - } - - private void ShowError(string message) { - this.OnError?.Invoke(message); - } - - private BitmapImage CreateQRFromUrl(string url) { - QRCodeGenerator qrGenerator = new QRCodeGenerator(); - QRCodeData qrCodeData = qrGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q); - QRCode qrCode = new QRCode(qrCodeData); - System.Drawing.Bitmap qrCodeImage = qrCode.GetGraphic(20); - - MemoryStream ms = new MemoryStream(); - ((System.Drawing.Bitmap)qrCodeImage).Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); - BitmapImage image = new BitmapImage(); - image.BeginInit(); - ms.Seek(0, SeekOrigin.Begin); - image.StreamSource = ms; - image.EndInit(); - return image; - } - - public void ShowSetup(ZitiIdentity identity, string url, string secret) { - SetupCode.Text = ""; - this.zid = identity; - MFAImage.Visibility = Visibility.Visible; - SecretCode.Visibility = Visibility.Collapsed; - CloseBlack.Visibility = Visibility.Visible; - CloseWhite.Visibility = Visibility.Collapsed; - SecretButton.Content = "Show Secret"; - IdName.Content = identity.Name; - Logger.Debug($"MFA Url: {url}"); - AuthBrush.Visibility = Visibility.Collapsed; - MainBrush.Visibility = Visibility.Visible; - - MFAImage.Source = CreateQRFromUrl(url); - SecretCode.Text = secret; - - _url = url; - MFAArea.Height = 515; - MFAAuthArea.Visibility = Visibility.Collapsed; - MFASetupArea.Visibility = Visibility.Visible; - MFARecoveryArea.Visibility = Visibility.Collapsed; - SeperationColor.Visibility = Visibility.Visible; - SetupCode.Focus(); - } - - public void ShowRecovery(string[] codes, ZitiIdentity identity) { - this.zid = identity; - MFASetupArea.Visibility = Visibility.Collapsed; - MFAAuthArea.Visibility = Visibility.Collapsed; - SeperationColor.Visibility = Visibility.Collapsed; - MFARecoveryArea.Visibility = Visibility.Visible; - RecoveryList.Children.Clear(); - _codes = codes; - AuthBrush.Visibility = Visibility.Collapsed; - MainBrush.Visibility = Visibility.Visible; - CloseBlack.Visibility = Visibility.Visible; - CloseWhite.Visibility = Visibility.Collapsed; - MFAArea.Height = 380; - if (codes.Length>0) { - for (int i = 0; i < codes.Length; i++) { - TextBox label = new TextBox(); - label.Text = codes[i]; - label.BorderThickness = new Thickness(0); - label.Background = new SolidColorBrush(); - label.Background.Opacity = 0; - label.HorizontalAlignment = HorizontalAlignment.Center; - label.HorizontalContentAlignment = HorizontalAlignment.Center; - label.VerticalAlignment = VerticalAlignment.Center; - label.VerticalContentAlignment = VerticalAlignment.Center; - RecoveryList.Children.Add(label); - } - RecoveryList.Visibility = Visibility.Visible; - NoRecovery.Visibility = Visibility.Collapsed; - SaveButton.Visibility = Visibility.Visible; - } else { - - ShowMFA(this.zid, 2); - SaveButton.Visibility = Visibility.Collapsed; - RecoveryList.Visibility = Visibility.Collapsed; - NoRecovery.Visibility = Visibility.Visible; - } - } - - public void ShowMFA(ZitiIdentity identity, int type) { - this.Type = type; - AuthCode.Text = ""; - AuthBrush.Visibility = Visibility.Visible; - MainBrush.Visibility = Visibility.Collapsed; - CloseBlack.Visibility = Visibility.Collapsed; - CloseWhite.Visibility = Visibility.Visible; - this.zid = identity; - MFASetupArea.Visibility = Visibility.Collapsed; - MFARecoveryArea.Visibility = Visibility.Collapsed; - SeperationColor.Visibility = Visibility.Collapsed; - MFAAuthArea.Visibility = Visibility.Visible; - MFAArea.Height = 220; - AuthCode.Focusable = true; - AuthCode.Focus(); - } - - private BitmapImage LoadImage(string url) { - var imgUrl = new Uri(url); - var imageData = new WebClient().DownloadData(imgUrl); - var bitmapImage = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad }; - bitmapImage.BeginInit(); - bitmapImage.StreamSource = new MemoryStream(imageData); - bitmapImage.EndInit(); - return bitmapImage; - } - - private void GoTo(object sender, MouseButtonEventArgs e) { - if (_url!=null&&_url.Length>0) { - Process.Start(new ProcessStartInfo(_url) { UseShellExecute = true }); - } else { - this.OnError?.Invoke("Invalid MFA Url"); - } - } - - async private void GetMFACodes(object sender, MouseButtonEventArgs e) { - DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; - string code = AuthCode.Text; - Logger.Debug("AuthMFA successful."); - MfaRecoveryCodesResponse getcodes = await serviceClient.GetMFACodes(this.zid.Identifier, code); - if (getcodes.Code != 0) { - Logger.Error("AuthMFA failed. " + getcodes.Error); - } - Logger.Error("DATA: {0}", getcodes.Data); - } - async private void GenerateMFACodes(object sender, MouseButtonEventArgs e) { - DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; - string code = AuthCode.Text; - MfaRecoveryCodesResponse gencodes = await serviceClient.GenerateMFACodes(this.zid.Identifier, code); - if (gencodes.Code != 0) { - Logger.Error("AuthMFA failed. " + gencodes.Error); - } - Logger.Error("DATA: {0}", gencodes.Data); - } - - private void SaveCodes(object sender, MouseButtonEventArgs e) { - string fileText = string.Join("\n", _codes); - string name = Regex.Replace(this.zid.Name, "[^a-zA-Z0-9]", String.Empty); - - System.Windows.Forms.SaveFileDialog dialog = new System.Windows.Forms.SaveFileDialog(); - dialog.Filter = "Text Files(*.txt)|*.txt|All(*.*)|*"; - dialog.Title = "Save Recovery Codes"; - dialog.FileName = name + "RecoveryCodes.txt"; - - if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.Cancel) { - File.WriteAllText(dialog.FileName, fileText); - } - } - - async private void DoSetupAuthenticate(object sender, MouseButtonEventArgs e) { - string code = SetupCode.Text; - - DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; - SvcResponse resp = await serviceClient.VerifyMFA(this.zid.Identifier, code); - if (resp.Code != 0) { - this.OnClose?.Invoke(false); - } else { - this.OnClose?.Invoke(false); - } - } - - /// - /// Call the MFA Functions - /// - /// Types: - /// 1 = Normal MFA Authentication - /// 2 = Get Recovery Codes - /// 3 = Remove MFA - /// 4 = Generate New MFA Codes - /// - async private void DoAuthenticate(object sender, MouseButtonEventArgs e) { - if (!this._executing) { - - this._executing = true; - string code = AuthCode.Text; - - if (code.Trim().Length>0) { - - DataClient serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; - this.OnLoad?.Invoke(false, "Authentication", "One Moment Please..."); - if (this.Type == 1) { - SvcResponse authResult = await serviceClient.AuthMFA(this.zid.Identifier, code); - if (authResult?.Code != 0) { - Logger.Error("AuthMFA failed. " + authResult.Error); - this.OnError?.Invoke("Authentication Failed"); - this._executing = false; - } else { - this.zid.IsMFANeeded = true; - this.OnClose?.Invoke(true); - this._executing = false; - } - this.OnLoad?.Invoke(true, "", ""); - } else if (this.Type == 2) { - MfaRecoveryCodesResponse codeResponse = await serviceClient.GetMFACodes(this.zid.Identifier, code); - if (codeResponse?.Code != 0) { - Logger.Error("AuthMFA failed. " + codeResponse.Error); - AuthCode.Text = ""; - this.OnError?.Invoke("Authentication Failed"); - this._executing = false; - } else { - this.zid.RecoveryCodes = codeResponse.Data.RecoveryCodes; - this.OnClose?.Invoke(true); - this._executing = false; - } - this.OnLoad?.Invoke(true, "", ""); - } else if (this.Type == 3) { - SvcResponse authResult = await serviceClient.RemoveMFA(this.zid.Identifier, code); - if (authResult?.Code != 0) { - Logger.Error("AuthMFA failed. " + authResult.Error); - AuthCode.Text = ""; - this.OnError?.Invoke("Authentication Failed"); - this._executing = false; - } else { - this.OnClose?.Invoke(true); - this._executing = false; - } - this.OnLoad?.Invoke(true, "", ""); - } else if (this.Type == 4) { - MfaRecoveryCodesResponse codeResponse = await serviceClient.GenerateMFACodes(this.zid.Identifier, code); - if (codeResponse?.Code != 0) { - Logger.Error("AuthMFA failed. " + codeResponse?.Error); - AuthCode.Text = ""; - this.OnError?.Invoke("Authentication Failed"); - } else { - this.zid.RecoveryCodes = codeResponse.Data.RecoveryCodes; - this.OnClose?.Invoke(true); - } - this._executing = false; - this.OnLoad?.Invoke(true, "", ""); - } - } - } - } - - private void RegenerateCodes(object sender, MouseButtonEventArgs e) { - ShowMFA(this.zid, 4); - } - - private void ShowSecret(object sender, MouseButtonEventArgs e) { - if (SecretCode.Visibility==Visibility.Visible) { - MFAImage.Visibility = Visibility.Visible; - SecretCode.Visibility = Visibility.Collapsed; - SecretButton.Content = "Show Secret"; - } else { - MFAImage.Visibility = Visibility.Collapsed; - SecretCode.Visibility = Visibility.Visible; - SecretButton.Content = "Show QR Code"; - } - } - - private void HandleKey(object sender, KeyEventArgs e) { - if (e.Key == Key.Return) { - DoSetupAuthenticate(sender, null); - } - } - - private void AuthCode_KeyUp(object sender, KeyEventArgs e) { - if (e.Key == Key.Return) { - DoAuthenticate(sender, null); - } - } - } + /// + /// Interaction logic for MFA.xaml + /// + public partial class MFAScreen : UserControl { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public delegate void LoadEvent(bool isComplete, string title, string message); + public event LoadEvent OnLoad; + public delegate void CloseAction(bool isComplete); + public event CloseAction OnClose; + private string _url = ""; + public delegate void ErrorOccurred(string message); + public event ErrorOccurred OnError; + private string[] _codes = new string[0]; + private ZitiIdentity zid; + private bool _executing = false; + public int Type { get; set; } + + public ZitiIdentity Identity { + get { + return this.zid; + } + set { + this.zid = value; + } + } + + public MFAScreen() { + InitializeComponent(); + } + + private void ExecuteClose(object sender, MouseButtonEventArgs e) { + this.OnClose?.Invoke(false); + } + + private void ShowError(string message) { + this.OnError?.Invoke(message); + } + + private BitmapImage CreateQRFromUrl(string url) { + QRCodeGenerator qrGenerator = new QRCodeGenerator(); + QRCodeData qrCodeData = qrGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q); + QRCode qrCode = new QRCode(qrCodeData); + System.Drawing.Bitmap qrCodeImage = qrCode.GetGraphic(20); + + MemoryStream ms = new MemoryStream(); + ((System.Drawing.Bitmap)qrCodeImage).Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + BitmapImage image = new BitmapImage(); + image.BeginInit(); + ms.Seek(0, SeekOrigin.Begin); + image.StreamSource = ms; + image.EndInit(); + return image; + } + + public void ShowSetup(ZitiIdentity identity, string url, string secret) { + SetupCode.Text = ""; + this.zid = identity; + MFAImage.Visibility = Visibility.Visible; + SecretCode.Visibility = Visibility.Collapsed; + CloseBlack.Visibility = Visibility.Visible; + CloseWhite.Visibility = Visibility.Collapsed; + SecretButton.Content = "Show Secret"; + IdName.Content = identity.Name; + Logger.Debug($"MFA Url: {url}"); + AuthBrush.Visibility = Visibility.Collapsed; + MainBrush.Visibility = Visibility.Visible; + + MFAImage.Source = CreateQRFromUrl(url); + SecretCode.Text = secret; + + _url = url; + MFAArea.Height = 515; + MFAAuthArea.Visibility = Visibility.Collapsed; + MFASetupArea.Visibility = Visibility.Visible; + MFARecoveryArea.Visibility = Visibility.Collapsed; + SeperationColor.Visibility = Visibility.Visible; + SetupCode.Focus(); + } + + public void ShowRecovery(string[] codes, ZitiIdentity identity) { + this.zid = identity; + MFASetupArea.Visibility = Visibility.Collapsed; + MFAAuthArea.Visibility = Visibility.Collapsed; + SeperationColor.Visibility = Visibility.Collapsed; + MFARecoveryArea.Visibility = Visibility.Visible; + RecoveryList.Children.Clear(); + _codes = codes; + AuthBrush.Visibility = Visibility.Collapsed; + MainBrush.Visibility = Visibility.Visible; + CloseBlack.Visibility = Visibility.Visible; + CloseWhite.Visibility = Visibility.Collapsed; + MFAArea.Height = 380; + if (codes.Length > 0) { + for (int i = 0; i < codes.Length; i++) { + TextBox label = new TextBox(); + label.Text = codes[i]; + label.BorderThickness = new Thickness(0); + label.Background = new SolidColorBrush(); + label.Background.Opacity = 0; + label.HorizontalAlignment = HorizontalAlignment.Center; + label.HorizontalContentAlignment = HorizontalAlignment.Center; + label.VerticalAlignment = VerticalAlignment.Center; + label.VerticalContentAlignment = VerticalAlignment.Center; + RecoveryList.Children.Add(label); + } + RecoveryList.Visibility = Visibility.Visible; + NoRecovery.Visibility = Visibility.Collapsed; + SaveButton.Visibility = Visibility.Visible; + } else { + + ShowMFA(this.zid, 2); + SaveButton.Visibility = Visibility.Collapsed; + RecoveryList.Visibility = Visibility.Collapsed; + NoRecovery.Visibility = Visibility.Visible; + } + } + + public void ShowMFA(ZitiIdentity identity, int type) { + this.Type = type; + AuthCode.Text = ""; + AuthBrush.Visibility = Visibility.Visible; + MainBrush.Visibility = Visibility.Collapsed; + CloseBlack.Visibility = Visibility.Collapsed; + CloseWhite.Visibility = Visibility.Visible; + this.zid = identity; + MFASetupArea.Visibility = Visibility.Collapsed; + MFARecoveryArea.Visibility = Visibility.Collapsed; + SeperationColor.Visibility = Visibility.Collapsed; + MFAAuthArea.Visibility = Visibility.Visible; + MFAArea.Height = 220; + AuthCode.Focusable = true; + AuthCode.Focus(); + } + + private BitmapImage LoadImage(string url) { + var imgUrl = new Uri(url); + var imageData = new WebClient().DownloadData(imgUrl); + var bitmapImage = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad }; + bitmapImage.BeginInit(); + bitmapImage.StreamSource = new MemoryStream(imageData); + bitmapImage.EndInit(); + return bitmapImage; + } + + private void GoTo(object sender, MouseButtonEventArgs e) { + if (_url != null && _url.Length > 0) { + Process.Start(new ProcessStartInfo(_url) { UseShellExecute = true }); + } else { + this.OnError?.Invoke("Invalid MFA Url"); + } + } + + async private void GetMFACodes(object sender, MouseButtonEventArgs e) { + DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; + string code = AuthCode.Text; + Logger.Debug("AuthMFA successful."); + MfaRecoveryCodesResponse getcodes = await serviceClient.GetMFACodes(this.zid.Identifier, code); + if (getcodes.Code != 0) { + Logger.Error("AuthMFA failed. " + getcodes.Error); + } + Logger.Error("DATA: {0}", getcodes.Data); + } + async private void GenerateMFACodes(object sender, MouseButtonEventArgs e) { + DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; + string code = AuthCode.Text; + MfaRecoveryCodesResponse gencodes = await serviceClient.GenerateMFACodes(this.zid.Identifier, code); + if (gencodes.Code != 0) { + Logger.Error("AuthMFA failed. " + gencodes.Error); + } + Logger.Error("DATA: {0}", gencodes.Data); + } + + private void SaveCodes(object sender, MouseButtonEventArgs e) { + string fileText = string.Join("\n", _codes); + string name = Regex.Replace(this.zid.Name, "[^a-zA-Z0-9]", String.Empty); + + System.Windows.Forms.SaveFileDialog dialog = new System.Windows.Forms.SaveFileDialog(); + dialog.Filter = "Text Files(*.txt)|*.txt|All(*.*)|*"; + dialog.Title = "Save Recovery Codes"; + dialog.FileName = name + "RecoveryCodes.txt"; + + if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.Cancel) { + File.WriteAllText(dialog.FileName, fileText); + } + } + + async private void DoSetupAuthenticate(object sender, MouseButtonEventArgs e) { + string code = SetupCode.Text; + + DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; + SvcResponse resp = await serviceClient.VerifyMFA(this.zid.Identifier, code); + if (resp.Code != 0) { + this.OnClose?.Invoke(false); + } else { + this.OnClose?.Invoke(false); + } + } + + /// + /// Call the MFA Functions + /// + /// Types: + /// 1 = Normal MFA Authentication + /// 2 = Get Recovery Codes + /// 3 = Remove MFA + /// 4 = Generate New MFA Codes + /// + async private void DoAuthenticate(object sender, MouseButtonEventArgs e) { + if (!this._executing) { + + this._executing = true; + string code = AuthCode.Text; + + if (code.Trim().Length > 0) { + + DataClient serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; + this.OnLoad?.Invoke(false, "Authentication", "One Moment Please..."); + if (this.Type == 1) { + SvcResponse authResult = await serviceClient.AuthMFA(this.zid.Identifier, code); + if (authResult?.Code != 0) { + Logger.Error("AuthMFA failed. " + authResult.Error); + this.OnError?.Invoke("Authentication Failed"); + this._executing = false; + } else { + this.zid.IsMFANeeded = true; + this.OnClose?.Invoke(true); + this._executing = false; + } + this.OnLoad?.Invoke(true, "", ""); + } else if (this.Type == 2) { + MfaRecoveryCodesResponse codeResponse = await serviceClient.GetMFACodes(this.zid.Identifier, code); + if (codeResponse?.Code != 0) { + Logger.Error("AuthMFA failed. " + codeResponse.Error); + AuthCode.Text = ""; + this.OnError?.Invoke("Authentication Failed"); + this._executing = false; + } else { + this.zid.RecoveryCodes = codeResponse.Data.RecoveryCodes; + this.OnClose?.Invoke(true); + this._executing = false; + } + this.OnLoad?.Invoke(true, "", ""); + } else if (this.Type == 3) { + SvcResponse authResult = await serviceClient.RemoveMFA(this.zid.Identifier, code); + if (authResult?.Code != 0) { + Logger.Error("AuthMFA failed. " + authResult.Error); + AuthCode.Text = ""; + this.OnError?.Invoke("Authentication Failed"); + this._executing = false; + } else { + this.OnClose?.Invoke(true); + this._executing = false; + } + this.OnLoad?.Invoke(true, "", ""); + } else if (this.Type == 4) { + MfaRecoveryCodesResponse codeResponse = await serviceClient.GenerateMFACodes(this.zid.Identifier, code); + if (codeResponse?.Code != 0) { + Logger.Error("AuthMFA failed. " + codeResponse?.Error); + AuthCode.Text = ""; + this.OnError?.Invoke("Authentication Failed"); + } else { + this.zid.RecoveryCodes = codeResponse.Data.RecoveryCodes; + this.OnClose?.Invoke(true); + } + this._executing = false; + this.OnLoad?.Invoke(true, "", ""); + } + } + } + } + + private void RegenerateCodes(object sender, MouseButtonEventArgs e) { + ShowMFA(this.zid, 4); + } + + private void ShowSecret(object sender, MouseButtonEventArgs e) { + if (SecretCode.Visibility == Visibility.Visible) { + MFAImage.Visibility = Visibility.Visible; + SecretCode.Visibility = Visibility.Collapsed; + SecretButton.Content = "Show Secret"; + } else { + MFAImage.Visibility = Visibility.Collapsed; + SecretCode.Visibility = Visibility.Visible; + SecretButton.Content = "Show QR Code"; + } + } + + private void HandleKey(object sender, KeyEventArgs e) { + if (e.Key == Key.Return) { + DoSetupAuthenticate(sender, null); + } + } + + private void AuthCode_KeyUp(object sender, KeyEventArgs e) { + if (e.Key == Key.Return) { + DoAuthenticate(sender, null); + } + } + } } diff --git a/DesktopEdge/Views/Screens/MainMenu.xaml b/DesktopEdge/Views/Screens/MainMenu.xaml index 2e4e7ff89..4d20a3315 100644 --- a/DesktopEdge/Views/Screens/MainMenu.xaml +++ b/DesktopEdge/Views/Screens/MainMenu.xaml @@ -44,7 +44,7 @@ - + diff --git a/DesktopEdge/Views/Screens/MainMenu.xaml.cs b/DesktopEdge/Views/Screens/MainMenu.xaml.cs index 75bc8e557..0396d395a 100644 --- a/DesktopEdge/Views/Screens/MainMenu.xaml.cs +++ b/DesktopEdge/Views/Screens/MainMenu.xaml.cs @@ -1,19 +1,19 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -35,737 +35,747 @@ limitations under the License. using ZitiDesktopEdge.Utility; namespace ZitiDesktopEdge { - /// - /// Interaction logic for MainMenu.xaml - /// - public partial class MainMenu : UserControl { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public delegate void AttachementChanged(bool attached); - public event AttachementChanged OnAttachmentChange; - public delegate void LogLevelChanged(string level); - public event LogLevelChanged OnLogLevelChanged; - public delegate void Detched(MouseButtonEventArgs e); - public event Detched OnDetach; - public delegate void ShowBlurb(string message); - public event ShowBlurb OnShowBlurb; - public string menuState = "Main"; - public string licenseData = "it's open source."; - public string LogLevel = ""; - private string appVersion = null; - private bool allowReleaseSelect = false; - public double MainHeight = 500; - - private ZDEWViewState state; - - private bool isBeta { - get { - return Application.Current.Properties["ReleaseStream"]?.ToString() == "beta"; - } - } - - public void ShowUpdateAvailable() { - if (state.UpdateAvailable) { - ForceUpdate.Visibility = Visibility.Visible; - } - - if (state.PendingUpdate.TimeLeft > 0) { - UpdateTimeLeft.Visibility = Visibility.Visible; - if (!state.AutomaticUpdatesDisabled) { - UpdateTimeLeft.Content = $"Automatic update to {state.PendingUpdate.Version} will occur on or after {state.PendingUpdate.InstallTime.ToString("g")}"; + /// + /// Interaction logic for MainMenu.xaml + /// + public partial class MainMenu : UserControl { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public delegate void AttachementChanged(bool attached); + public event AttachementChanged OnAttachmentChange; + public delegate Task LogLevelChanged(string level); + public event LogLevelChanged OnLogLevelChanged; + public delegate void Detched(MouseButtonEventArgs e); + public event Detched OnDetach; + public delegate void ShowBlurb(string message); + public event ShowBlurb OnShowBlurb; + public string menuState = "Main"; + public string licenseData = "it's open source."; + public string LogLevel = ""; + private string appVersion = null; + public double MainHeight = 500; + + private ZDEWViewState state; + + public bool ShowUnexpectedFailure { get; set; } + + public void ShowUpdateAvailable() { + if (state.UpdateAvailable) { + ForceUpdate.Visibility = Visibility.Visible; + } + + if (state.PendingUpdate.TimeLeft > 0) { + UpdateTimeLeft.Visibility = Visibility.Visible; + if (!state.AutomaticUpdatesDisabled) { + UpdateTimeLeft.Content = $"Automatic update to {state.PendingUpdate.Version} will occur on or after {state.PendingUpdate.InstallTime.ToString("g")}"; CheckForUpdateStatus.Content = $"update {state.PendingUpdate.Version} is available"; } else { - UpdateTimeLeft.Content = ""; + UpdateTimeLeft.Content = ""; } CheckForUpdateStatus.Visibility = UpdateTimeLeft.Visibility; } - SetAutomaticUpgradesState(); - } - - internal MainWindow MainWindow { get; set; } - - public MainMenu() { - InitializeComponent(); - Application.Current.MainWindow.Title = "Ziti Desktop Edge"; - state = (ZDEWViewState)Application.Current.Properties["ZDEWViewState"]; - - try { - allowReleaseSelect = bool.Parse(ConfigurationManager.AppSettings.Get("ReleaseStreamSelect")); - } catch { - //if we can't parse the config - leave it as false... - allowReleaseSelect = false; //setting it here in case anyone changes the default above - } -#if DEBUG - Debug.WriteLine("OVERRIDING allowReleaseSelect to true!"); - allowReleaseSelect = true; -#endif - appVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - LicensesItems.Text = licenseData; - // don't check from the UI any more... CheckUpdates(); - } - - private void HideMenu(object sender, MouseButtonEventArgs e) { - menuState = "Menu"; - UpdateState(); - MainMenuArea.Visibility = Visibility.Collapsed; - } - private void Window_MouseDown(object sender, MouseButtonEventArgs e) { - if (e.ChangedButton == MouseButton.Left) { - OnDetach(e); - } - } - - private void CloseApp(object sender, MouseButtonEventArgs e) { - Application.Current.Shutdown(); - } - - private void ShowAbout(object sender, MouseButtonEventArgs e) { - menuState = "About"; - UpdateState(); - } - - private void ShowAdvanced(object sender, MouseButtonEventArgs e) { - menuState = "Advanced"; - UpdateState(); - } - private void ShowIdentities(object sender, MouseButtonEventArgs e) { - menuState = "Identities"; - UpdateState(); - } - private void ShowLicenses(object sender, MouseButtonEventArgs e) { - menuState = "Licenses"; - UpdateState(); - } - private void ShowConfig(object sender, MouseButtonEventArgs e) { - menuState = "Config"; - UpdateState(); - } - private void ShowLogs(object sender, MouseButtonEventArgs e) { - menuState = "Logs"; - UpdateState(); - } - private void ShowUILogs(object sender, MouseButtonEventArgs e) { - menuState = "UILogs"; - UpdateState(); - } - private void ShowReleaseStreamMenuAction(object sender, MouseButtonEventArgs e) { - logger.Warn("this is ShowReleaseStreamMenuAction at warn"); - logger.Info("this is ShowReleaseStreamMenuAction at info"); - logger.Debug("this is ShowReleaseStreamMenuAction at debug"); - logger.Trace("this is ShowReleaseStreamMenuAction at trace"); - menuState = "SetReleaseStream"; - UpdateState(); - } - - private void ShowAutomaticUpgradesMenuAction(object sender, MouseButtonEventArgs e) { - menuState = "ConfigureAutomaticUpgrades"; - UpdateState(); - } - - async private void SetAutomaticUpgradesMenuAction(object sender, MouseButtonEventArgs e) { - bool disableAutomaticUpgrades = false; - if (sender == AutomaticUpgradesItemOff) { - disableAutomaticUpgrades = true; - } - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - - SvcResponse r = await monitorClient.SetAutomaticUpgradeDisabledAsync(disableAutomaticUpgrades); - if (r.Code != 0) { - logger.Error(r?.Error); - } - SetAutomaticUpgradesState(); - } - - private void checkResponse(SvcResponse r, string titleOnErr, string msgOnErr) { - if (r == null) { - MainWindow.ShowError(titleOnErr, msgOnErr); - } else { - logger.Info(r?.ToString()); - } - } - - private void SetLogLevel(object sender, MouseButtonEventArgs e) { - menuState = "LogLevel"; - UpdateState(); - } - - private void UpdateState() { - double h = this.ActualHeight - 100.00; - if (h > 0) { - IdListScrollView.Height = h; - } - IdListScrollView.Visibility = Visibility.Collapsed; - MainItems.Visibility = Visibility.Collapsed; - AboutItems.Visibility = Visibility.Collapsed; - MainItemsButton.Visibility = Visibility.Collapsed; - AboutItemsArea.Visibility = Visibility.Collapsed; - BackArrow.Visibility = Visibility.Collapsed; - AdvancedItems.Visibility = Visibility.Collapsed; - LicensesItems.Visibility = Visibility.Collapsed; - LogsItems.Visibility = Visibility.Collapsed; - ConfigItems.Visibility = Visibility.Collapsed; - LogLevelItems.Visibility = Visibility.Collapsed; - AutomaticUpgradesItems.Visibility = Visibility.Collapsed; - Visibility visibilityFromUpdateAvail = state.UpdateAvailable ? Visibility.Visible : Visibility.Collapsed; - TriggerUpdateButton.Visibility = visibilityFromUpdateAvail; - ForceUpdate.Visibility = visibilityFromUpdateAvail; - CheckForUpdateStatus.Visibility = visibilityFromUpdateAvail; - - if (menuState == "About") { - MenuTitle.Content = "About"; - AboutItemsArea.Visibility = Visibility.Visible; - AboutItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - - string version = ""; - try { - TunnelStatus s = (TunnelStatus)Application.Current.Properties["CurrentTunnelStatus"]; - version = $"{s.ServiceVersion.Version}@{s.ServiceVersion.Revision}"; - } catch (Exception e) { - logger.Warn(e, "Could not get service version/revision?"); - } - - // Interface Version - VersionInfo.Content = $"App: {appVersion} Service: {version}"; - - } else if (menuState == "Advanced") { - MenuTitle.Content = "Advanced Settings"; - AdvancedItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "Licenses") { - MenuTitle.Content = "Third Party Licenses"; - LicensesItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "Logs") { - MenuTitle.Content = "Advanced Settings"; - AdvancedItems.Visibility = Visibility.Visible; - //string targetFile = NativeMethods.GetFinalPathName(MainWindow.ExpectedLogPathServices); - string targetFile = MainWindow.ExpectedLogPathServices; - - OpenLogFile("service", targetFile); - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "UILogs") { - MenuTitle.Content = "Advanced Settings"; - AdvancedItems.Visibility = Visibility.Visible; - OpenLogFile("UI", MainWindow.ExpectedLogPathUI); - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "LogLevel") { - ResetLevels(); - - MenuTitle.Content = "Set Log Level"; - LogLevelItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "ConfigureAutomaticUpgrades") { - SetAutomaticUpgradesState(); - - MenuTitle.Content = "Automatic Upgrades"; - AutomaticUpgradesItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "Config") { - MenuTitle.Content = "Tunnel Configuration"; - ConfigItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - - ConfigPageSize.Value = ((Application.Current.Properties.Contains("ApiPageSize")) ? Application.Current.Properties["ApiPageSize"].ToString() : "25"); - ConfigIp.Value = Application.Current.Properties["ip"]?.ToString(); - ConfigSubnet.Value = Application.Current.Properties["subnet"]?.ToString(); - ConfigMtu.Value = Application.Current.Properties["mtu"]?.ToString(); - ConfigDns.Value = Application.Current.Properties["dns"]?.ToString(); - ConfigDnsEnabled.Value = Application.Current.Properties["dnsenabled"]?.ToString(); - } else if (menuState == "Identities") { - MenuTitle.Content = "Identities"; - IdListScrollView.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else { - MenuTitle.Content = "Main Menu"; - MainItems.Visibility = Visibility.Visible; - MainItemsButton.Visibility = Visibility.Visible; - } - - // ShowUpdateAvailable(); - } - - private void OpenLogFile(string which, string logFile) { - var whichRoot = Path.Combine(MainWindow.ExpectedLogPathRoot, which); - try { - string target = Native.NativeMethods.GetFinalPathName(logFile); - if (File.Exists(target)) { - logger.Info("opening {0} logs at: {1}", which, target); - var p = Process.Start(new ProcessStartInfo(target) { UseShellExecute = true }); - if (p != null) { - logger.Info("showing {0} logs. file: {1}", which, target); - } else { - Process.Start(whichRoot); - } - return; - } else { - logger.Warn("could not show {0} logs. file not found: {1}", which, target); - } - } catch { - } - Process.Start(whichRoot); - } - - private void GoBack(object sender, MouseButtonEventArgs e) { - if (menuState == "Config" || menuState == "LogLevel" || menuState == "UILogs" || menuState == "SetReleaseStream" || menuState == "ConfigureAutomaticUpgrades") { - menuState = "Advanced"; - } else if (menuState == "Licenses") { - menuState = "About"; - } else { - menuState = "Menu"; - } - UpdateState(); - } - private void ShowPrivacy(object sender, MouseButtonEventArgs e) { - Process.Start(new ProcessStartInfo("https://netfoundry.io/privacy") { UseShellExecute = true }); - } - private void ShowTerms(object sender, MouseButtonEventArgs e) { - Process.Start(new ProcessStartInfo("https://netfoundry.io/terms") { UseShellExecute = true }); - } - - async private void ShowFeedback(object sender, MouseButtonEventArgs e) { - try { - MainWindow.ShowLoad("Collecting Information", "Please wait while we run some commands\nand collect some diagnostic information"); - - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - sb.Append("Logs collected at : " + DateTime.Now.ToString()); - sb.Append(". client version : " + appVersion); - - string timestamp = DateTime.Now.ToFileTime().ToString(); - - var dataClient = (DataClient)Application.Current.Properties["ServiceClient"]; - - string exeLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - string logLocation = Path.Combine(exeLocation, "logs"); - string serviceLogsLocation = Path.Combine(logLocation, "service"); - await dataClient.zitiDump(serviceLogsLocation); - - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); - if (resp == null) { - logger.Error("no response from monitorClient?"); - MainWindow mw = (MainWindow)Application.Current.MainWindow; - mw?.ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback.\nIs the monitor service running?"); - return; - } - string pathToLogs = resp.Message; - logger.Info("Log files found at : {0}", resp.Message); - string args = string.Format("/Select, \"{0}\"", pathToLogs); - - ProcessStartInfo pfi = new ProcessStartInfo("Explorer.exe", args); - Process.Start(pfi); - } catch (Exception ex) { - logger.Warn(ex, "An unexpected error has occurred when submitting feedback? {0}", ex.Message); - } - MainWindow.HideLoad(); - } - - private void ShowSupport(object sender, MouseButtonEventArgs e) { - Process.Start(new ProcessStartInfo("https://openziti.discourse.group/") { UseShellExecute = true }); - } - - private void DetachWindow(object sender, MouseButtonEventArgs e) { - Application.Current.MainWindow.ShowInTaskbar = true; - DetachButton.Visibility = Visibility.Collapsed; - AttachButton.Visibility = Visibility.Visible; - Arrow.Visibility = Visibility.Collapsed; - if (OnAttachmentChange != null) { - OnAttachmentChange(false); - } - MainMenuArea.Visibility = Visibility.Collapsed; - } - - public void Detach() { - Application.Current.MainWindow.ShowInTaskbar = true; - DetachButton.Visibility = Visibility.Collapsed; - AttachButton.Visibility = Visibility.Visible; - Arrow.Visibility = Visibility.Collapsed; - } - private void RetachWindow(object sender, MouseButtonEventArgs e) { - Application.Current.MainWindow.ShowInTaskbar = false; - DetachButton.Visibility = Visibility.Visible; - AttachButton.Visibility = Visibility.Collapsed; - Arrow.Visibility = Visibility.Visible; - if (OnAttachmentChange != null) { - OnAttachmentChange(true); - } - } - - private void ResetLevels() { - if (this.LogLevel == "") this.LogLevel = "error"; - LogVerbose.IsSelected = false; - LogDebug.IsSelected = false; - LogInfo.IsSelected = false; - LogError.IsSelected = false; - LogWarn.IsSelected = false; - LogTrace.IsSelected = false; - if (this.LogLevel == "verbose") LogVerbose.IsSelected = true; - else if (this.LogLevel == "debug") LogDebug.IsSelected = true; - else if (this.LogLevel == "info") LogInfo.IsSelected = true; - else if (this.LogLevel == "error") LogError.IsSelected = true; - else if (this.LogLevel == "warn") LogWarn.IsSelected = true; - else if (this.LogLevel == "trace") LogTrace.IsSelected = true; - } - - private void SetLevel(object sender, MouseButtonEventArgs e) { - SubOptionItem item = (SubOptionItem)sender; - this.LogLevel = item.Label.ToLower(); - if (OnLogLevelChanged != null) { - OnLogLevelChanged(this.LogLevel); - } - ResetLevels(); - } - - private void SetAutomaticUpgradesState() { - bool disabled = state.AutomaticUpdatesDisabled; - this.AutomaticUpgradesItemOn.IsSelected = !disabled; - this.AutomaticUpgradesItemOff.IsSelected = disabled; - this.UpdateUrl.Text = state.AutomaticUpdateURL; - } - - async private void CheckForUpdate_OnClick(object sender, MouseButtonEventArgs e) { - logger.Info("checking for update..."); - CheckForUpdateStatus.Content = "Checking for updates..."; - await Task.Delay(1000); - if (state.AutomaticUpdateURL != this.UpdateUrl.Text) { - SetUpdateUrlButton_Click(sender, e); - } - try { - CheckForUpdate.IsEnabled = false; - CheckForUpdateStatus.Visibility = Visibility.Visible; - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - var r = await monitorClient.DoUpdateCheck(); - checkResponse(r, "Error When Checking for Update", "An error occurred while trying check for update."); - CheckForUpdateStatus.Content = r.Message; - if (r.UpdateAvailable) { - TriggerUpdateButton.Visibility = Visibility.Visible; - ForceUpdate.Visibility = Visibility.Visible; - } else { - TriggerUpdateButton.Visibility = Visibility.Collapsed; - ForceUpdate.Visibility = Visibility.Collapsed; - } - } catch (Exception ex) { - logger.Error(ex, "unexpected error in update check: {0}", ex.Message); - } - CheckForUpdate.IsEnabled = true; - } - - async private void TriggerUpdate_RoutedEventArgs_Click(object sender, RoutedEventArgs e) { - Button src = null; - if (sender is Button) { - src = sender as Button; - } - try { - CheckForUpdateStatus.Content = "Requesting automatic update..."; - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - var r = await monitorClient.TriggerUpdate(); - CheckForUpdateStatus.Content = "Automatic update requested..."; - if (r == null) { - MainWindow.ShowError("Error When Triggering Update", "An error occurred while trying to trigger the update."); - if (src != null) src.IsEnabled = true; - } else { - this.OnShowBlurb?.Invoke("Update Requested"); - if (src != null) src.Content = "Request Update Again"; - UpdateTimeLeft.Content = "Update Requested at " + DateTime.Now; - TriggerUpdateButton.Visibility = Visibility.Collapsed; - logger.Info(r?.ToString()); - menuState = "Menu"; - UpdateState(); - MainMenuArea.Visibility = Visibility.Collapsed; - } - } catch (Exception ex) { - logger.Error(ex, "unexpected error in update check: {0}", ex.Message); - MainWindow.ShowError("Error When Triggering Update", "An error occurred while trying to trigger the update."); - if (src != null) src.IsEnabled = true; - } - } - - public void SetupIdList(ZitiIdentity[] ids) { - IdListView.Children.Clear(); - for (int i = 0; i < ids.Length; i++) { - MenuIdentityItem item = new MenuIdentityItem(); - item.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch; - item.Label = ids[i].Name; - item.Identity = ids[i]; - item.ToggleSwitch.Enabled = ids[i].IsEnabled; - IdListView.Children.Add(item); - } - } - - public void SetAppUpgradeAvailableText(string msg) { - this.Dispatcher.Invoke(() => { - VersionOlder.Content = msg; - VersionNewer.Content = ""; - VersionOlder.Visibility = Visibility.Visible; - VersionNewer.Visibility = Visibility.Collapsed; - }); - } - public void SetAppIsNewer(string msg) { - this.Dispatcher.Invoke(() => { - VersionNewer.Content = msg; - VersionOlder.Content = ""; - VersionNewer.Visibility = Visibility.Visible; - VersionOlder.Visibility = Visibility.Collapsed; - }); - } - - public void Disconnected() { - ConfigItems.IsEnabled = false; - ConfigItems.Opacity = 0.3; - LogLevelItems.IsEnabled = false; - LogLevelItems.Opacity = 0.3; - } - - public void Connected() { - ConfigItems.IsEnabled = true; - ConfigItems.Opacity = 1.0; - LogLevelItems.IsEnabled = true; - LogLevelItems.Opacity = 1.0; - } - - /// - /// Save the config information to the properties and queue for update. - /// - async private void UpdateConfig() { - logger.Info("updating config..."); - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - try { - ComboBoxItem item = (ComboBoxItem)ConfigMaskNew.SelectedValue; - var newMaskVar = Int32.Parse(item.Tag.ToString()); - var addDnsNewVar = Convert.ToBoolean(AddDnsNew.IsChecked); - CheckRange(); - int pageSize = Int32.Parse(ConfigePageSizeNew.Text); - ConfigPageSize.Value = ConfigePageSizeNew.Text; - - var r = await client.UpdateConfigAsync(ConfigIpNew.Text, newMaskVar, addDnsNewVar, pageSize); - if (r.Code != 0) { - this.OnShowBlurb?.Invoke("Error: " + r.Error); - logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); - } else { - this.OnShowBlurb?.Invoke("Config Save, Please Restart Ziti to Update"); - this.CloseEdit(); - } - logger.Info("Got response from update config task : {0}", r); - } catch (DataStructures.ServiceException se) { - this.OnShowBlurb?.Invoke("Error: " + se.Message); - logger.Error(se, "service exception in update check: {0}", se.Message); - } catch (Exception ex) { - this.OnShowBlurb?.Invoke("Error: " + ex.Message); - logger.Error(ex, "unexpected error in update check: {0}", ex.Message); - } - } - - /// - /// Save the frequency information to the properties and queue for update. - /// - async private void UpdateFrequency() { - logger.Info("updating frequency..."); - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - try { - var newFrequencyVar = Int32.Parse(Frequency.Text); - - var r = await client.NotificationFrequencyPayloadAsync(newFrequencyVar); - if (r.Code != 0) { - this.OnShowBlurb?.Invoke("Error: " + r.Error); - logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); - } else { - this.OnShowBlurb?.Invoke("Frequency Saved"); - this.CloseFrequency(); - } - logger.Info("Got response from update frequency task : {0}", r); - } catch (DataStructures.ServiceException se) { - this.OnShowBlurb?.Invoke("Error: " + se.Message); - logger.Error(se, "service exception in update frequency check: {0}", se.Message); - } catch (Exception ex) { - this.OnShowBlurb?.Invoke("Error: " + ex.Message); - logger.Error(ex, "unexpected error in update frequency check: {0}", ex.Message); - } - } - - - /// - /// Show the Edit Modal and blur the background - /// - private void ShowEdit_Click(object sender, MouseButtonEventArgs e) { - ConfigIpNew.Text = ConfigIp.Value; - ConfigePageSizeNew.Text = ConfigPageSize.Value; - CheckRange(); - for (int i = 0; i < ConfigMaskNew.Items.Count; i++) { - ComboBoxItem item = (ComboBoxItem)ConfigMaskNew.Items.GetItemAt(i); - if (item.Content.ToString().IndexOf(ConfigSubnet.Value) > 0) { - ConfigMaskNew.SelectedIndex = i; - break; - } - } - AddDnsNew.IsChecked = false; - if (Application.Current.Properties.Contains("dnsenabled")) { - AddDnsNew.IsChecked = (bool)Application.Current.Properties["dnsenabled"]; - } - EditArea.Opacity = 0; - EditArea.Visibility = Visibility.Visible; - EditArea.Margin = new Thickness(0, 0, 0, 0); - EditArea.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); - EditArea.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - ShowModal(); - } - - /// - /// Show the Frequency Modal and blur the background - /// - private void ShowFrequency() { - Frequency.Text = ""; - FrequencyArea.Opacity = 0; - FrequencyArea.Visibility = Visibility.Visible; - FrequencyArea.Margin = new Thickness(0, 0, 0, 0); - FrequencyArea.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); - FrequencyArea.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - ShowModal(); - } - - /// - /// Hide the Edit Config - /// - private void CloseFrequency() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); - animation.Completed += CloseFrequencyComplete; - FrequencyArea.BeginAnimation(Grid.OpacityProperty, animation); - FrequencyArea.BeginAnimation(Grid.MarginProperty, animateThick); - HideModal(); - } - - /// - /// Close the config window - /// - /// The close button - /// The event arguments - private void CloseFrequencyComplete(object sender, EventArgs e) { - FrequencyArea.Visibility = Visibility.Collapsed; - } - - /// - /// Hide the Edit Config - /// - private void CloseEdit() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); - animation.Completed += CloseComplete; - EditArea.BeginAnimation(Grid.OpacityProperty, animation); - EditArea.BeginAnimation(Grid.MarginProperty, animateThick); - HideModal(); - } - - /// - /// Show the modal, aniimating opacity - /// - private void ShowModal() { - ModalBg.Visibility = Visibility.Visible; - ModalBg.Opacity = 0; - DoubleAnimation animation = new DoubleAnimation(.8, TimeSpan.FromSeconds(.3)); - ModalBg.BeginAnimation(Grid.OpacityProperty, animation); - } - - /// - /// Close the config window - /// - /// The close button - /// The event arguments - private void CloseComplete(object sender, EventArgs e) { - EditArea.Visibility = Visibility.Collapsed; - } - - /// - /// Hide the modal animating the opacity - /// - private void HideModal() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - animation.Completed += ModalHideComplete; - ModalBg.BeginAnimation(Grid.OpacityProperty, animation); - } - - /// - /// When the animation completes, set the visibility to avoid UI object conflicts - /// - /// The animation - /// The event - private void ModalHideComplete(object sender, EventArgs e) { - ModalBg.Visibility = Visibility.Collapsed; - } - - /// - /// Close the config editor without saving - /// - /// The image button - /// The click event - private void CloseEditConfig(object sender, MouseButtonEventArgs e) { - CloseEdit(); - } - - private void SaveConfig_Click(object sender, MouseButtonEventArgs e) { - this.UpdateConfig(); - } - - private void SaveFrequencyButton_OnClick(object sender, MouseButtonEventArgs e) { - UpdateFrequency(); - } - - private void CloseFrequencyArea(object sender, MouseButtonEventArgs e) { - CloseFrequency(); - } - - private void EditFreqButton_OnClick(object sender, MouseButtonEventArgs e) { - ShowFrequency(); - } - - private void Frequency_KeyUp(object sender, KeyEventArgs e) { - if (e.Key == Key.Return) { - UpdateFrequency(); - } - } - - private void ConfigePageSizeNew_KeyDown(object sender, KeyEventArgs e) { - } - - private void ConfigePageSizeNew_LostFocus(object sender, RoutedEventArgs e) { - CheckRange(); - } - - private void CheckRange() { - int defaultVal = 250; - string setVal = ConfigePageSizeNew.Text; - int value = defaultVal; - if (Int32.TryParse(ConfigePageSizeNew.Text, out value)) { - } - if (value < 10 || value > 500) value = defaultVal; - ConfigePageSizeNew.Text = value.ToString(); - } - - private void ResetUrlButton_Click(object sender, RoutedEventArgs e) { - UpdateUrl.Text = GithubAPI.ProdUrl; - } - - private async void SetUpdateUrlButton_Click(object sender, MouseButtonEventArgs e) { - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - - SvcResponse r = await monitorClient.SetAutomaticUpgradeURLAsync(UpdateUrl.Text); - if (r == null) { - logger.Error("Failed to set automatic upgrade url! SvcResponse was null?!?!?"); - MainWindow.ShowError("Could not set url!", "Is the monitor service running?"); - } else if (r.Code != 0) { - logger.Error(r?.Error); - MainWindow.ShowError("Could not set url", r?.Error); - } else { - this.OnShowBlurb?.Invoke("Config Saved."); - } - } - - private void UpdateUrl_TextChanged(object sender, TextChangedEventArgs e) { - logger.Info("url: {}", state.AutomaticUpdateURL); - } - - private void Button_Click(object sender, RoutedEventArgs e) { - logger.Info("sender name: {}", sender); - - } - } + SetAutomaticUpgradesState(); + } + + internal MainWindow MainWindow { get; set; } + + public MainMenu() { + InitializeComponent(); + Application.Current.MainWindow.Title = "Ziti Desktop Edge"; + state = (ZDEWViewState)Application.Current.Properties["ZDEWViewState"]; + + try { + ShowUnexpectedFailure = bool.Parse(ConfigurationManager.AppSettings.Get("ShowUnexpectedFailure")); + } catch { + //if we can't parse the config - leave it as false... + ShowUnexpectedFailure = false; //setting it here in case anyone changes the default above + } + + appVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + LicensesItems.Text = licenseData; + // don't check from the UI any more... CheckUpdates(); + } + + private void HideMenu(object sender, MouseButtonEventArgs e) { + menuState = "Menu"; + UpdateState(); + MainMenuArea.Visibility = Visibility.Collapsed; + } + private void Window_MouseDown(object sender, MouseButtonEventArgs e) { + if (e.ChangedButton == MouseButton.Left) { + OnDetach(e); + } + } + + private void CloseApp(object sender, MouseButtonEventArgs e) { + Application.Current.Shutdown(); + } + + private void ShowAbout(object sender, MouseButtonEventArgs e) { + menuState = "About"; + UpdateState(); + } + + private void ShowAdvanced(object sender, MouseButtonEventArgs e) { + menuState = "Advanced"; + UpdateState(); + } + private void ShowIdentities(object sender, MouseButtonEventArgs e) { + menuState = "Identities"; + UpdateState(); + } + private void ShowLicenses(object sender, MouseButtonEventArgs e) { + menuState = "Licenses"; + UpdateState(); + } + private void ShowConfig(object sender, MouseButtonEventArgs e) { + menuState = "Config"; + UpdateState(); + } + private void ShowLogs(object sender, MouseButtonEventArgs e) { + menuState = "Logs"; + UpdateState(); + } + private void ShowUILogs(object sender, MouseButtonEventArgs e) { + menuState = "UILogs"; + UpdateState(); + } + private void ShowReleaseStreamMenuAction(object sender, MouseButtonEventArgs e) { + logger.Warn("this is ShowReleaseStreamMenuAction at warn"); + logger.Info("this is ShowReleaseStreamMenuAction at info"); + logger.Debug("this is ShowReleaseStreamMenuAction at debug"); + logger.Trace("this is ShowReleaseStreamMenuAction at trace"); + menuState = "SetReleaseStream"; + UpdateState(); + } + + private void ShowAutomaticUpgradesMenuAction(object sender, MouseButtonEventArgs e) { + menuState = "ConfigureAutomaticUpgrades"; + UpdateState(); + } + + async private void SetAutomaticUpgradesMenuAction(object sender, MouseButtonEventArgs e) { + bool disableAutomaticUpgrades = false; + if (sender == AutomaticUpgradesItemOff) { + disableAutomaticUpgrades = true; + } + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + try { + SvcResponse r = await monitorClient.SetAutomaticUpgradeDisabledAsync(disableAutomaticUpgrades); + if (r.Code != 0) { + logger.Error(r?.Error); + } + } catch (MonitorServiceException) { + MainWindow.ShowError("Could Not Set Automatic Update", "The monitor service is offline"); + } catch (Exception ex) { + logger.Error("unexpected error when setting automatic upgrade enabled", ex); + } + SetAutomaticUpgradesState(); + } + + private void checkResponse(SvcResponse r, string titleOnErr, string msgOnErr) { + if (r == null) { + MainWindow.ShowError(titleOnErr, msgOnErr); + } else { + logger.Info(r?.ToString()); + } + } + + private void SetLogLevel(object sender, MouseButtonEventArgs e) { + menuState = "LogLevel"; + UpdateState(); + } + + private void UpdateState() { + double h = this.ActualHeight - 100.00; + if (h > 0) { + IdListScrollView.Height = h; + } + IdListScrollView.Visibility = Visibility.Collapsed; + MainItems.Visibility = Visibility.Collapsed; + AboutItems.Visibility = Visibility.Collapsed; + MainItemsButton.Visibility = Visibility.Collapsed; + AboutItemsArea.Visibility = Visibility.Collapsed; + BackArrow.Visibility = Visibility.Collapsed; + AdvancedItems.Visibility = Visibility.Collapsed; + LicensesItems.Visibility = Visibility.Collapsed; + LogsItems.Visibility = Visibility.Collapsed; + ConfigItems.Visibility = Visibility.Collapsed; + LogLevelItems.Visibility = Visibility.Collapsed; + AutomaticUpgradesItems.Visibility = Visibility.Collapsed; + Visibility visibilityFromUpdateAvail = state.UpdateAvailable ? Visibility.Visible : Visibility.Collapsed; + TriggerUpdateButton.Visibility = visibilityFromUpdateAvail; + ForceUpdate.Visibility = visibilityFromUpdateAvail; + CheckForUpdateStatus.Visibility = visibilityFromUpdateAvail; + + if (menuState == "About") { + MenuTitle.Content = "About"; + AboutItemsArea.Visibility = Visibility.Visible; + AboutItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + + string version = ""; + try { + TunnelStatus s = (TunnelStatus)Application.Current.Properties["CurrentTunnelStatus"]; + version = $"{s.ServiceVersion.Version}@{s.ServiceVersion.Revision}"; + } catch (Exception e) { + logger.Warn(e, "Could not get service version/revision?"); + } + + // Interface Version + VersionInfo.Content = $"App: {appVersion} Service: {version}"; + + } else if (menuState == "Advanced") { + MenuTitle.Content = "Advanced Settings"; + AdvancedItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "Licenses") { + MenuTitle.Content = "Third Party Licenses"; + LicensesItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "Logs") { + MenuTitle.Content = "Advanced Settings"; + AdvancedItems.Visibility = Visibility.Visible; + //string targetFile = NativeMethods.GetFinalPathName(MainWindow.ExpectedLogPathServices); + string targetFile = MainWindow.ExpectedLogPathServices; + + OpenLogFile("service", targetFile); + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "UILogs") { + MenuTitle.Content = "Advanced Settings"; + AdvancedItems.Visibility = Visibility.Visible; + OpenLogFile("UI", MainWindow.ExpectedLogPathUI); + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "LogLevel") { + ResetLevels(); + + MenuTitle.Content = "Set Log Level"; + LogLevelItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "ConfigureAutomaticUpgrades") { + SetAutomaticUpgradesState(); + + MenuTitle.Content = "Automatic Upgrades"; + AutomaticUpgradesItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "Config") { + MenuTitle.Content = "Tunnel Configuration"; + ConfigItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + + ConfigPageSize.Value = ((Application.Current.Properties.Contains("ApiPageSize")) ? Application.Current.Properties["ApiPageSize"].ToString() : "25"); + ConfigIp.Value = Application.Current.Properties["ip"]?.ToString(); + ConfigSubnet.Value = Application.Current.Properties["subnet"]?.ToString(); + ConfigMtu.Value = Application.Current.Properties["mtu"]?.ToString(); + ConfigDns.Value = Application.Current.Properties["dns"]?.ToString(); + ConfigDnsEnabled.Value = Application.Current.Properties["dnsenabled"]?.ToString(); + } else if (menuState == "Identities") { + MenuTitle.Content = "Identities"; + IdListScrollView.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else { + MenuTitle.Content = "Main Menu"; + MainItems.Visibility = Visibility.Visible; + MainItemsButton.Visibility = Visibility.Visible; + } + + // ShowUpdateAvailable(); + } + + private void OpenLogFile(string which, string logFile) { + var whichRoot = Path.Combine(MainWindow.ExpectedLogPathRoot, which); + try { + string target = Native.NativeMethods.GetFinalPathName(logFile); + if (File.Exists(target)) { + logger.Info("opening {0} logs at: {1}", which, target); + var p = Process.Start(new ProcessStartInfo(target) { UseShellExecute = true }); + if (p != null) { + logger.Info("showing {0} logs. file: {1}", which, target); + } else { + Process.Start(whichRoot); + } + return; + } else { + logger.Warn("could not show {0} logs. file not found: {1}", which, target); + } + } catch { + } + Process.Start(whichRoot); + } + + private void GoBack(object sender, MouseButtonEventArgs e) { + if (menuState == "Config" || menuState == "LogLevel" || menuState == "UILogs" || menuState == "SetReleaseStream" || menuState == "ConfigureAutomaticUpgrades") { + menuState = "Advanced"; + } else if (menuState == "Licenses") { + menuState = "About"; + } else { + menuState = "Menu"; + } + UpdateState(); + } + private void ShowPrivacy(object sender, MouseButtonEventArgs e) { + Process.Start(new ProcessStartInfo("https://netfoundry.io/privacy") { UseShellExecute = true }); + } + private void ShowTerms(object sender, MouseButtonEventArgs e) { + Process.Start(new ProcessStartInfo("https://netfoundry.io/terms") { UseShellExecute = true }); + } + + async public void CollectFeedbackLogs(object sender, MouseButtonEventArgs e) { + try { + MainWindow.ShowLoad("Collecting Information", "Please wait while we run some commands\nand collect some diagnostic information"); + + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + sb.Append("Logs collected at : " + DateTime.Now.ToString()); + sb.Append(". client version : " + appVersion); + + string timestamp = DateTime.Now.ToFileTime().ToString(); + + var dataClient = (DataClient)Application.Current.Properties["ServiceClient"]; + + string exeLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + string logLocation = Path.Combine(exeLocation, "logs"); + string serviceLogsLocation = Path.Combine(logLocation, "service"); + await dataClient.zitiDump(serviceLogsLocation); + + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); + if (resp == null) { + logger.Error("no response from monitorClient?"); + MainWindow mw = (MainWindow)Application.Current.MainWindow; + mw?.ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback.\nIs the monitor service running?"); + return; + } + string pathToLogs = resp.Message; + logger.Info("Log files found at : {0}", resp.Message); + string args = string.Format("/Select, \"{0}\"", pathToLogs); + + ProcessStartInfo pfi = new ProcessStartInfo("Explorer.exe", args); + Process.Start(pfi); + } catch (MonitorServiceException) { + MainWindow.ShowError("Could Not Collect Feedback", "The monitor service is offline"); + } catch (Exception ex) { + logger.Warn(ex, "An unexpected error has occurred when submitting feedback? {0}", ex.Message); + MainWindow.ShowError("Could Not Collect Feedback", "The monitor service is offline"); + } + MainWindow.HideLoad(); + } + + private void ShowSupport(object sender, MouseButtonEventArgs e) { + Process.Start(new ProcessStartInfo("https://openziti.discourse.group/") { UseShellExecute = true }); + } + + private void DetachWindow(object sender, MouseButtonEventArgs e) { + Application.Current.MainWindow.ShowInTaskbar = true; + DetachButton.Visibility = Visibility.Collapsed; + AttachButton.Visibility = Visibility.Visible; + Arrow.Visibility = Visibility.Collapsed; + if (OnAttachmentChange != null) { + OnAttachmentChange(false); + } + MainMenuArea.Visibility = Visibility.Collapsed; + } + + public void Detach() { + Application.Current.MainWindow.ShowInTaskbar = true; + DetachButton.Visibility = Visibility.Collapsed; + AttachButton.Visibility = Visibility.Visible; + Arrow.Visibility = Visibility.Collapsed; + } + private void RetachWindow(object sender, MouseButtonEventArgs e) { + Application.Current.MainWindow.ShowInTaskbar = false; + DetachButton.Visibility = Visibility.Visible; + AttachButton.Visibility = Visibility.Collapsed; + Arrow.Visibility = Visibility.Visible; + if (OnAttachmentChange != null) { + OnAttachmentChange(true); + } + } + + private void ResetLevels() { + if (this.LogLevel == "") this.LogLevel = "error"; + LogVerbose.IsSelected = false; + LogDebug.IsSelected = false; + LogInfo.IsSelected = false; + LogError.IsSelected = false; + LogWarn.IsSelected = false; + LogTrace.IsSelected = false; + if (this.LogLevel == "verbose") LogVerbose.IsSelected = true; + else if (this.LogLevel == "debug") LogDebug.IsSelected = true; + else if (this.LogLevel == "info") LogInfo.IsSelected = true; + else if (this.LogLevel == "error") LogError.IsSelected = true; + else if (this.LogLevel == "warn") LogWarn.IsSelected = true; + else if (this.LogLevel == "trace") LogTrace.IsSelected = true; + } + + async private void SetLevel(object sender, MouseButtonEventArgs e) { + SubOptionItem item = (SubOptionItem)sender; + if (OnLogLevelChanged != null) { + if (await OnLogLevelChanged(item.Label.ToLower())) { + this.LogLevel = item.Label.ToLower(); + } + } + ResetLevels(); + } + + private void SetAutomaticUpgradesState() { + bool disabled = state.AutomaticUpdatesDisabled; + this.AutomaticUpgradesItemOn.IsSelected = !disabled; + this.AutomaticUpgradesItemOff.IsSelected = disabled; + this.UpdateUrl.Text = state.AutomaticUpdateURL; + } + + async private void CheckForUpdate_OnClick(object sender, MouseButtonEventArgs e) { + logger.Info("checking for update..."); + CheckForUpdateStatus.Content = "Checking for updates..."; + await Task.Delay(1000); + if (state.AutomaticUpdateURL != this.UpdateUrl.Text) { + SetUpdateUrlButton_Click(sender, e); + } + try { + CheckForUpdate.IsEnabled = false; + CheckForUpdateStatus.Visibility = Visibility.Visible; + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + var r = await monitorClient.DoUpdateCheck(); + checkResponse(r, "Error When Checking for Update", "An error occurred while trying check for update."); + CheckForUpdateStatus.Content = r.Message; + if (r.UpdateAvailable) { + TriggerUpdateButton.Visibility = Visibility.Visible; + ForceUpdate.Visibility = Visibility.Visible; + } else { + TriggerUpdateButton.Visibility = Visibility.Collapsed; + ForceUpdate.Visibility = Visibility.Collapsed; + } + } catch (MonitorServiceException) { + CheckForUpdateStatus.Content = "Monitor service is offline"; + } catch (Exception ex) { + logger.Error(ex, "unexpected error in update check: {0}", ex.Message); + CheckForUpdateStatus.Content = "Error checking for updates. See logs."; + } + CheckForUpdate.IsEnabled = true; + } + + async private void TriggerUpdate_RoutedEventArgs_Click(object sender, RoutedEventArgs e) { + Button src = null; + if (sender is Button) { + src = sender as Button; + } + try { + CheckForUpdateStatus.Content = "Requesting automatic update..."; + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + var r = await monitorClient.TriggerUpdate(); + CheckForUpdateStatus.Content = "Automatic update requested..."; + if (r == null) { + MainWindow.ShowError("Error When Triggering Update", "An error occurred while trying to trigger the update."); + if (src != null) src.IsEnabled = true; + } else { + this.OnShowBlurb?.Invoke("Update Requested"); + if (src != null) src.Content = "Request Update Again"; + UpdateTimeLeft.Content = "Update Requested at " + DateTime.Now; + TriggerUpdateButton.Visibility = Visibility.Collapsed; + logger.Info(r?.ToString()); + menuState = "Menu"; + UpdateState(); + MainMenuArea.Visibility = Visibility.Collapsed; + } + } catch (Exception ex) { + logger.Error(ex, "unexpected error in update check: {0}", ex.Message); + MainWindow.ShowError("Error When Triggering Update", "An error occurred while trying to trigger the update."); + if (src != null) src.IsEnabled = true; + } + } + + public void SetupIdList(ZitiIdentity[] ids) { + IdListView.Children.Clear(); + for (int i = 0; i < ids.Length; i++) { + MenuIdentityItem item = new MenuIdentityItem(); + item.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch; + item.Label = ids[i].Name; + item.Identity = ids[i]; + item.ToggleSwitch.Enabled = ids[i].IsEnabled; + IdListView.Children.Add(item); + } + } + + public void SetAppUpgradeAvailableText(string msg) { + this.Dispatcher.Invoke(() => { + VersionOlder.Content = msg; + VersionNewer.Content = ""; + VersionOlder.Visibility = Visibility.Visible; + VersionNewer.Visibility = Visibility.Collapsed; + }); + } + public void SetAppIsNewer(string msg) { + this.Dispatcher.Invoke(() => { + VersionNewer.Content = msg; + VersionOlder.Content = ""; + VersionNewer.Visibility = Visibility.Visible; + VersionOlder.Visibility = Visibility.Collapsed; + }); + } + + public void Disconnected() { + ConfigItems.IsEnabled = false; + ConfigItems.Opacity = 0.3; + LogLevelItems.IsEnabled = false; + LogLevelItems.Opacity = 0.3; + } + + public void Connected() { + ConfigItems.IsEnabled = true; + ConfigItems.Opacity = 1.0; + LogLevelItems.IsEnabled = true; + LogLevelItems.Opacity = 1.0; + } + + /// + /// Save the config information to the properties and queue for update. + /// + async private void UpdateConfig() { + logger.Info("updating config..."); + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + try { + ComboBoxItem item = (ComboBoxItem)ConfigMaskNew.SelectedValue; + var newMaskVar = Int32.Parse(item.Tag.ToString()); + var addDnsNewVar = Convert.ToBoolean(AddDnsNew.IsChecked); + CheckRange(); + int pageSize = Int32.Parse(ConfigePageSizeNew.Text); + ConfigPageSize.Value = ConfigePageSizeNew.Text; + + var r = await client.UpdateConfigAsync(ConfigIpNew.Text, newMaskVar, addDnsNewVar, pageSize); + if (r.Code != 0) { + this.OnShowBlurb?.Invoke("Error: " + r.Error); + logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); + } else { + this.OnShowBlurb?.Invoke("Config Save, Please Restart Ziti to Update"); + this.CloseEdit(); + } + logger.Info("Got response from update config task : {0}", r); + } catch (DataStructures.ServiceException se) { + this.OnShowBlurb?.Invoke("Error: " + se.Message); + logger.Error(se, "service exception in update check: {0}", se.Message); + } catch (Exception ex) { + this.OnShowBlurb?.Invoke("Error: " + ex.Message); + logger.Error(ex, "unexpected error in update check: {0}", ex.Message); + } + } + + /// + /// Save the frequency information to the properties and queue for update. + /// + async private void UpdateFrequency() { + logger.Info("updating frequency..."); + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + try { + var newFrequencyVar = Int32.Parse(Frequency.Text); + + var r = await client.NotificationFrequencyPayloadAsync(newFrequencyVar); + if (r.Code != 0) { + this.OnShowBlurb?.Invoke("Error: " + r.Error); + logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); + } else { + this.OnShowBlurb?.Invoke("Frequency Saved"); + this.CloseFrequency(); + } + logger.Info("Got response from update frequency task : {0}", r); + } catch (DataStructures.ServiceException se) { + this.OnShowBlurb?.Invoke("Error: " + se.Message); + logger.Error(se, "service exception in update frequency check: {0}", se.Message); + } catch (Exception ex) { + this.OnShowBlurb?.Invoke("Error: " + ex.Message); + logger.Error(ex, "unexpected error in update frequency check: {0}", ex.Message); + } + } + + + /// + /// Show the Edit Modal and blur the background + /// + private void ShowEdit_Click(object sender, MouseButtonEventArgs e) { + ConfigIpNew.Text = ConfigIp.Value; + ConfigePageSizeNew.Text = ConfigPageSize.Value; + CheckRange(); + for (int i = 0; i < ConfigMaskNew.Items.Count; i++) { + ComboBoxItem item = (ComboBoxItem)ConfigMaskNew.Items.GetItemAt(i); + if (item.Content.ToString().IndexOf(ConfigSubnet.Value) > 0) { + ConfigMaskNew.SelectedIndex = i; + break; + } + } + AddDnsNew.IsChecked = false; + if (Application.Current.Properties.Contains("dnsenabled")) { + AddDnsNew.IsChecked = (bool)Application.Current.Properties["dnsenabled"]; + } + EditArea.Opacity = 0; + EditArea.Visibility = Visibility.Visible; + EditArea.Margin = new Thickness(0, 0, 0, 0); + EditArea.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + EditArea.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + ShowModal(); + } + + /// + /// Show the Frequency Modal and blur the background + /// + private void ShowFrequency() { + Frequency.Text = ""; + FrequencyArea.Opacity = 0; + FrequencyArea.Visibility = Visibility.Visible; + FrequencyArea.Margin = new Thickness(0, 0, 0, 0); + FrequencyArea.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + FrequencyArea.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + ShowModal(); + } + + /// + /// Hide the Edit Config + /// + private void CloseFrequency() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + animation.Completed += CloseFrequencyComplete; + FrequencyArea.BeginAnimation(Grid.OpacityProperty, animation); + FrequencyArea.BeginAnimation(Grid.MarginProperty, animateThick); + HideModal(); + } + + /// + /// Close the config window + /// + /// The close button + /// The event arguments + private void CloseFrequencyComplete(object sender, EventArgs e) { + FrequencyArea.Visibility = Visibility.Collapsed; + } + + /// + /// Hide the Edit Config + /// + private void CloseEdit() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + animation.Completed += CloseComplete; + EditArea.BeginAnimation(Grid.OpacityProperty, animation); + EditArea.BeginAnimation(Grid.MarginProperty, animateThick); + HideModal(); + } + + /// + /// Show the modal, aniimating opacity + /// + private void ShowModal() { + ModalBg.Visibility = Visibility.Visible; + ModalBg.Opacity = 0; + DoubleAnimation animation = new DoubleAnimation(.8, TimeSpan.FromSeconds(.3)); + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// Close the config window + /// + /// The close button + /// The event arguments + private void CloseComplete(object sender, EventArgs e) { + EditArea.Visibility = Visibility.Collapsed; + } + + /// + /// Hide the modal animating the opacity + /// + private void HideModal() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + animation.Completed += ModalHideComplete; + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// When the animation completes, set the visibility to avoid UI object conflicts + /// + /// The animation + /// The event + private void ModalHideComplete(object sender, EventArgs e) { + ModalBg.Visibility = Visibility.Collapsed; + } + + /// + /// Close the config editor without saving + /// + /// The image button + /// The click event + private void CloseEditConfig(object sender, MouseButtonEventArgs e) { + CloseEdit(); + } + + private void SaveConfig_Click(object sender, MouseButtonEventArgs e) { + this.UpdateConfig(); + } + + private void SaveFrequencyButton_OnClick(object sender, MouseButtonEventArgs e) { + UpdateFrequency(); + } + + private void CloseFrequencyArea(object sender, MouseButtonEventArgs e) { + CloseFrequency(); + } + + private void EditFreqButton_OnClick(object sender, MouseButtonEventArgs e) { + ShowFrequency(); + } + + private void Frequency_KeyUp(object sender, KeyEventArgs e) { + if (e.Key == Key.Return) { + UpdateFrequency(); + } + } + + private void ConfigePageSizeNew_KeyDown(object sender, KeyEventArgs e) { + } + + private void ConfigePageSizeNew_LostFocus(object sender, RoutedEventArgs e) { + CheckRange(); + } + + private void CheckRange() { + int defaultVal = 250; + string setVal = ConfigePageSizeNew.Text; + int value = defaultVal; + if (Int32.TryParse(ConfigePageSizeNew.Text, out value)) { + } + if (value < 10 || value > 500) value = defaultVal; + ConfigePageSizeNew.Text = value.ToString(); + } + + private void ResetUrlButton_Click(object sender, RoutedEventArgs e) { + UpdateUrl.Text = GithubAPI.ProdUrl; + } + + private async void SetUpdateUrlButton_Click(object sender, MouseButtonEventArgs e) { + try { + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + + SvcResponse r = await monitorClient.SetAutomaticUpgradeURLAsync(UpdateUrl.Text); + if (r == null) { + logger.Error("Failed to set automatic upgrade url! SvcResponse was null?!?!?"); + MainWindow.ShowError("Could not set url!", "Is the monitor service running?"); + } else if (r.Code != 0) { + logger.Error(r?.Error); + MainWindow.ShowError("Could not set url", r?.Error); + } else { + this.OnShowBlurb?.Invoke("Config Saved."); + } + } catch (MonitorServiceException) { + MainWindow.ShowError("Could Not Set URL", "The monitor service is offline"); + } catch (Exception ex) { + logger.Error("unexpected error when setting automatic upgrade enabled", ex); + } + } + + private void UpdateUrl_TextChanged(object sender, TextChangedEventArgs e) { + logger.Info("url: {}", state.AutomaticUpdateURL); + } + + private void Button_Click(object sender, RoutedEventArgs e) { + logger.Info("sender name: {}", sender); + + } + } } \ No newline at end of file diff --git a/DesktopEdge/ZitiDesktopEdge.csproj b/DesktopEdge/ZitiDesktopEdge.csproj index c31bc92f0..287d33b80 100644 --- a/DesktopEdge/ZitiDesktopEdge.csproj +++ b/DesktopEdge/ZitiDesktopEdge.csproj @@ -475,6 +475,7 @@ + diff --git a/Installer/build.ps1 b/Installer/build.ps1 index 351da359e..23347a831 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -9,7 +9,6 @@ param( ) $ErrorActionPreference = "Stop" - function verifyFile($path) { if (Test-Path -Path "$path") { "OK: $path exists!" diff --git a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs index 30e7254d4..6d551706d 100644 --- a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs +++ b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Linq; using System.Collections.Generic; @@ -33,84 +33,68 @@ public enum LogLevelEnum { TRACE = 6, } - public class SvcResponse - { + public class SvcResponse { public int Code { get; set; } public string Message { get; set; } public string Error { get; set; } - override public string ToString() - { + override public string ToString() { return $"Code: {Code}\nMessage: {Message}\nError: {Error}"; } } - public class StatusUpdateResponse : SvcResponse - { + public class StatusUpdateResponse : SvcResponse { public StatusUpdate Data { get; set; } } - public class StatusUpdate - { + public class StatusUpdate { public string Operation { get; set; } public TunnelStatus Status { get; set; } public Identity NewIdentity { get; set; } } - public class NewIdentity - { + public class NewIdentity { public EnrollmentFlags Flags { get; set; } public Identity Id { get; set; } } - public class IdentityResponse : SvcResponse - { + public class IdentityResponse : SvcResponse { public Identity Data { get; set; } } - public class ServiceFunction - { + public class ServiceFunction { public string Command { get; set; } } - public class IdentifierFunction : ServiceFunction - { + public class IdentifierFunction : ServiceFunction { public IdentifierPayload Data { get; set; } } - public class BooleanPayload - { + public class BooleanPayload { public bool OnOff { get; set; } } - public class BooleanFunction : ServiceFunction - { - public BooleanFunction(string commandName, bool theBool) - { + public class BooleanFunction : ServiceFunction { + public BooleanFunction(string commandName, bool theBool) { this.Command = commandName; this.Data = new BooleanPayload() { OnOff = theBool }; } public BooleanPayload Data { get; set; } } - public class IdentityTogglePayload - { + public class IdentityTogglePayload { public bool OnOff { get; set; } public string Identifier { get; set; } } - public class SetLogLevelPayload - { + public class SetLogLevelPayload { public string Level { get; set; } } - public class IdentityToggleFunction : ServiceFunction - { - public IdentityToggleFunction(string identifier, bool theBool) - { + public class IdentityToggleFunction : ServiceFunction { + public IdentityToggleFunction(string identifier, bool theBool) { this.Command = "IdentityOnOff"; - this.Data = new IdentityTogglePayload() - { + this.Data = new IdentityTogglePayload() { OnOff = theBool, Identifier = identifier, }; @@ -217,9 +201,7 @@ public SetLogLevelFunction(string level) { public class ZitiDumpPayloadFunction { public string DumpPath { get; set; } - } - public class ZitiDumpFunction : ServiceFunction { public ZitiDumpFunction(string dumpPath) { this.Command = "ZitiDump"; @@ -230,37 +212,51 @@ public ZitiDumpFunction(string dumpPath) { public ZitiDumpPayloadFunction Data { get; set; } } - public class IdentifierPayload - { + public class ExternalAuthFunction { + public string Identifier { get; set; } + } + public class ExternalAuthLogin : ServiceFunction { + public ExternalAuthLogin(string identifier) { + this.Command = "ExternalAuth"; + this.Data = new ExternalAuthFunction() { + Identifier = identifier, + }; + } + public ExternalAuthFunction Data { get; set; } + } + public class ExternalAuthLoginResponse : SvcResponse { + public ExternalAuthLoginPayload Data { get; set; } + } + public class ExternalAuthLoginPayload { + public string identifier { get; set; } + public string url { get; set; } + } + + public class IdentifierPayload { public string Identifier { get; set; } } - public class EnrollIdentifierPayload - { + public class EnrollIdentifierPayload { public string JwtFileName { get; set; } public string JwtContent { get; set; } - } + } - public class EnrollIdentifierFunction : ServiceFunction - { + public class EnrollIdentifierFunction : ServiceFunction { public EnrollIdentifierPayload Data { get; set; } } - public class Id - { + public class Id { public string key { get; set; } public string cert { get; set; } } - public class Config - { + public class Config { public string ztAPI { get; set; } public Id id { get; set; } public object configTypes { get; set; } } - public class Metrics - { + public class Metrics { public int TotalBytes { get; set; } public long Up { get; set; } public long Down { get; set; } @@ -281,7 +277,7 @@ public class Identity { public int MinTimeout { get; set; } public int MaxTimeout { get; set; } public DateTime MfaLastUpdatedTime { get; set; } - + public bool NeedsExtAuth { get; set; } } public class Service { @@ -293,9 +289,9 @@ public class Service { public string AssignedIP { get; set; } public PostureCheck[] PostureChecks { get; set; } public bool IsAccessible { get; set; } - public int Timeout { get; set; } - public int TimeoutRemaining { get; set; } - } + public int Timeout { get; set; } + public int TimeoutRemaining { get; set; } + } public class Address { public bool IsHost { get; set; } @@ -333,30 +329,26 @@ public class PostureCheck { public string Id { get; set; } } - public class EnrollmentFlags - { + public class EnrollmentFlags { public string JwtString { get; set; } public string CertFile { get; set; } public string KeyFile { get; set; } public string AdditionalCAs { get; set; } } - public class IpInfo - { + public class IpInfo { public string Ip { get; set; } public string Subnet { get; set; } public UInt16 MTU { get; set; } public string DNS { get; set; } } - public class ServiceVersion - { + public class ServiceVersion { public string Version { get; set; } public string Revision { get; set; } public string BuildDate { get; set; } - public override string ToString() - { + public override string ToString() { return $"Version: {Version}, Revision: {Revision}, BuildDate: {BuildDate}"; } } @@ -373,15 +365,11 @@ public class Notification { } - public class ZitiTunnelStatus : SvcResponse - { + public class ZitiTunnelStatus : SvcResponse { public TunnelStatus Status { get; set; } } - public class TunnelStatus - { - public bool Active { get; set; } - + public class TunnelStatus { public long Duration { get; set; } public List Identities { get; set; } @@ -394,10 +382,8 @@ public class TunnelStatus public bool AddDns { get; set; } public int ApiPageSize { get; set; } - public void Dump(System.IO.TextWriter writer) - { + public void Dump(System.IO.TextWriter writer) { try { - writer.WriteLine($"Tunnel Active: {Active}"); writer.WriteLine($" LogLevel : {LogLevel}"); writer.WriteLine($" EvaluatedLogLevel: {EvaluateLogLevel()}"); foreach (Identity id in Identities) { @@ -406,39 +392,31 @@ public void Dump(System.IO.TextWriter writer) writer.WriteLine($" Active : {id.Active}"); writer.WriteLine($" Status : {id.Status}"); writer.WriteLine($" Services:"); - if (id.Services != null) - { - foreach (Service s in id?.Services) - { - //xxfix writer.WriteLine($" Name: {s.Name} Protocols: {string.Join(",", s.Protocols)} Addresses: {string.Join(",", s.Addresses)} Ports: {string.Join(",", s.Ports)}"); + if (id.Services != null) { + foreach (Service s in id?.Services) { + //xxfix writer.WriteLine($" Name: {s.Name} Protocols: {string.Join(",", s.Protocols)} Addresses: {string.Join(",", s.Addresses)} Ports: {string.Join(",", s.Ports)}"); } } writer.WriteLine("============================================="); } } catch (Exception e) { - if (writer!=null) writer.WriteLine(e.ToString()); - } - + if (writer != null) writer.WriteLine(e.ToString()); + } + } - public LogLevelEnum EvaluateLogLevel() - { - try - { - LogLevelEnum l = (LogLevelEnum) Enum.Parse(typeof(LogLevelEnum), LogLevel.ToUpper()); + public LogLevelEnum EvaluateLogLevel() { + try { + LogLevelEnum l = (LogLevelEnum)Enum.Parse(typeof(LogLevelEnum), LogLevel.ToUpper()); return l; - } - catch - { + } catch { return LogLevelEnum.INFO; } } } - public class ServiceException : System.Exception - { - public ServiceException(string Message, int Code, string AdditionalInfo) : base(Message) - { + public class ServiceException : System.Exception { + public ServiceException(string Message, int Code, string AdditionalInfo) : base(Message) { this.Code = Code; this.AdditionalInfo = AdditionalInfo; } @@ -447,23 +425,19 @@ public ServiceException(string Message, int Code, string AdditionalInfo) : base( public string AdditionalInfo { get; } } - public class StatusEvent - { + public class StatusEvent { public string Op { get; set; } } - public class ActionEvent : StatusEvent - { + public class ActionEvent : StatusEvent { public string Action { get; set; } } - public class TunnelStatusEvent : StatusEvent - { + public class TunnelStatusEvent : StatusEvent { public TunnelStatus Status { get; set; } } - public class MetricsEvent : StatusEvent - { + public class MetricsEvent : StatusEvent { public List Identities { get; set; } } @@ -483,13 +457,11 @@ public class BulkServiceEvent : ActionEvent { public List RemovedServices { get; set; } } - public class IdentityEvent : ActionEvent - { + public class IdentityEvent : ActionEvent { public Identity Id { get; set; } } - - public class LogLevelEvent : ActionEvent - { + + public class LogLevelEvent : ActionEvent { public string LogLevel { get; set; } } @@ -541,21 +513,17 @@ public class MfaRecoveryCodesResponse : SvcResponse { public MfaRecoveryCodes Data { get; set; } } - public class ConfigPayload - { + public class ConfigPayload { public string TunIPv4 { get; set; } public int TunPrefixLength { get; set; } public bool AddDns { get; set; } public int ApiPageSize { get; set; } } - public class ConfigUpdateFunction : ServiceFunction - { - public ConfigUpdateFunction(string tunIPv4, int tunPrefixLength, bool addDns, int apiPageSize) - { + public class ConfigUpdateFunction : ServiceFunction { + public ConfigUpdateFunction(string tunIPv4, int tunPrefixLength, bool addDns, int apiPageSize) { this.Command = "UpdateTunIpv4"; - this.Data = new ConfigPayload() - { + this.Data = new ConfigPayload() { TunIPv4 = tunIPv4, TunPrefixLength = tunPrefixLength, AddDns = addDns, @@ -576,9 +544,9 @@ public NotificationFrequencyFunction(int notificationFrequency) { NotificationFrequency = notificationFrequency }; } - + public NotificationFrequencyPayload Data { get; set; } - } + } } diff --git a/ZitiDesktopEdge.Client/Properties/AssemblyInfo.cs b/ZitiDesktopEdge.Client/Properties/AssemblyInfo.cs index 958e1ef5f..90e18f2bf 100644 --- a/ZitiDesktopEdge.Client/Properties/AssemblyInfo.cs +++ b/ZitiDesktopEdge.Client/Properties/AssemblyInfo.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System.Reflection; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/ZitiDesktopEdge.Client/Server/EventRegistry.cs b/ZitiDesktopEdge.Client/Server/EventRegistry.cs index 816306bce..fcd27202d 100644 --- a/ZitiDesktopEdge.Client/Server/EventRegistry.cs +++ b/ZitiDesktopEdge.Client/Server/EventRegistry.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using Newtonsoft.Json; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using Newtonsoft.Json; using System; using System.Threading; diff --git a/ZitiDesktopEdge.Client/Server/IPCServer.cs b/ZitiDesktopEdge.Client/Server/IPCServer.cs index ee934314e..36ac31df2 100644 --- a/ZitiDesktopEdge.Client/Server/IPCServer.cs +++ b/ZitiDesktopEdge.Client/Server/IPCServer.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Threading.Tasks; using System.IO; using System.IO.Pipes; @@ -88,12 +88,12 @@ async public Task startIpcServerAsync(OnClientAsync onClient) { pipeSecurity); await ipcPipeServer.WaitForConnectionAsync(); - + Logger.Debug("Total ipc clients now at: {0}", ++idx); _ = Task.Run(async () => { try { await handleIpcClientAsync(ipcPipeServer, onClient); - } catch(Exception icpe) { + } catch (Exception icpe) { Logger.Error(icpe, "Unexpected error in handleIpcClientAsync"); } idx--; @@ -167,18 +167,22 @@ async public Task handleIpcClientAsync(NamedPipeServerStream ss, OnClientAsync o } async public Task handleEventClientAsync(NamedPipeServerStream ss, OnClientAsync onClient) { - using (ss) { - StreamWriter writer = new StreamWriter(ss); + try { + using (ss) { + StreamWriter writer = new StreamWriter(ss); - EventHandler eh = async (object sender, EventArgs e) => { - await writer.WriteLineAsync(sender.ToString()); - await writer.FlushAsync(); - }; + EventHandler eh = async (object sender, EventArgs e) => { + try { + await writer.WriteLineAsync(sender.ToString()); + await writer.FlushAsync(); + } catch (Exception ex) { + Logger.Error("problem with event handler in handleEventClientAsync: {}", ex.Message); + } + }; - await onClient(writer); + await onClient(writer); - EventRegistry.MyEvent += eh; - try { + EventRegistry.MyEvent += eh; StreamReader reader = new StreamReader(ss); string line = await reader.ReadLineAsync(); @@ -188,10 +192,10 @@ async public Task handleEventClientAsync(NamedPipeServerStream ss, OnClientAsync } Logger.Debug("handleEventClientAsync is complete"); - } catch (Exception e) { - Logger.Error(e, "Unexpected error when reading from or writing to a client pipe."); + EventRegistry.MyEvent -= eh; } - EventRegistry.MyEvent -= eh; + } catch (Exception e) { + Logger.Error(e, "Unexpected error when reading from or writing to a client pipe."); } } @@ -216,7 +220,7 @@ async public Task processMessageAsync(string json, StreamWriter writer) { break; } - foreach(var p in procs) { + foreach (var p in procs) { Logger.Warn("Forcefully terminating process: {0}", p.Id); p.Kill(); } diff --git a/ZitiDesktopEdge.Client/Server/ServiceActions.cs b/ZitiDesktopEdge.Client/Server/ServiceActions.cs index 08cbd7a89..29b9d4f0d 100644 --- a/ZitiDesktopEdge.Client/Server/ServiceActions.cs +++ b/ZitiDesktopEdge.Client/Server/ServiceActions.cs @@ -1,22 +1,22 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ using System; using System.Diagnostics; -using System.Linq; +using System.Linq; using System.Management; using System.ServiceProcess; using NLog; diff --git a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs index 37a1d25a3..c9171c528 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections.Generic; using System.IO; using System.IO.Pipes; @@ -59,28 +59,29 @@ public abstract class AbstractClient { protected virtual void ClientConnected(object e) { Connected = true; Reconnecting = false; - CleanShutdown = false; - Logger.Debug("Client connected successfully. Setting CleanShutdown set to false."); + ExpectedShutdown = false; + Logger.Debug("Client connected successfully. Setting UnexpectedShutdown set to false."); ipcWriter = new StreamWriter(pipeClient); ipcReader = new StreamReader(pipeClient); Task.Run(async () => { //hack for now until it's async... try { - StreamReader eventReader = new StreamReader(eventClient); - while (true) { - if (eventReader.EndOfStream) { - break; - } - string respAsString = null; - try { - respAsString = await readMessageAsync(eventReader); + using (StreamReader eventReader = new StreamReader(eventClient)) { + while (true) { + if (eventReader.EndOfStream) { + break; + } + string respAsString = null; try { - ProcessLine(respAsString); + respAsString = await readMessageAsync(eventReader); + try { + ProcessLine(respAsString); + } catch (Exception ex) { + Logger.Warn(ex, "ERROR caught in ProcessLine: {0}", respAsString); + } } catch (Exception ex) { - Logger.Warn(ex, "ERROR caught in ProcessLine: {0}", respAsString); + Logger.Warn(ex, "ERROR caught in readMessageAsync: {0}", respAsString); } - } catch (Exception ex) { - Logger.Warn(ex, "ERROR caught in readMessageAsync: {0}", respAsString); } } } catch (Exception ex) { @@ -101,7 +102,7 @@ protected virtual void ClientDisconnected(object e) { } protected virtual void ShutdownEvent(StatusEvent e) { - CleanShutdown = true; + ExpectedShutdown = true; OnShutdownEvent?.Invoke(this, e); } @@ -130,7 +131,7 @@ async protected Task sendAsync(object objToSend) { await ipcWriter.WriteAsync('\n'); await ipcWriter.FlushAsync(); } else { - throw new Exception("the monitor service appears to be offline?"); + throw new IPCException("ipcWriter is null. the target appears to be offline?"); } } else { Logger.Debug("NOT sending empty object??? " + objToSend?.ToString()); @@ -145,6 +146,8 @@ async protected Task sendAsync(object objToSend) { } else { retried = true; //fall back through to the while and try again } + } catch (MonitorServiceException) { + throw; } catch (Exception ex) { //if this fails it's usually because the writer is null/invalid. throwing IOException //will trigger the pipe to rebuild @@ -155,7 +158,7 @@ async protected Task sendAsync(object objToSend) { public bool Reconnecting { get; set; } public bool Connected { get; set; } - public bool CleanShutdown { get; set; } + public bool ExpectedShutdown { get; set; } public AbstractClient(string id) { this.Id = id; @@ -196,7 +199,7 @@ public void Reconnect() { } catch (Exception) { try { ReconnectFailureEvent("reconnect failure"); - } catch (Exception){ + } catch (Exception) { // don't care - just catch it and continue... it's a timeout... } var now = DateTime.Now; @@ -287,4 +290,17 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ return property; } } + + public class MonitorServiceException : Exception { + public MonitorServiceException() { } + public MonitorServiceException(string message) : base(message) { } + public MonitorServiceException(string message, Exception source) : base(message, source) { } + } + + public class IPCException : Exception { + public IPCException() { } + public IPCException(string message) : base(message) { } + public IPCException(string message, Exception source) : base(message, source) { } + + } } diff --git a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs index cd1a70f46..032478f04 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs @@ -1,19 +1,19 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + using System; using System.Collections.Generic; using System.IO; @@ -55,7 +55,7 @@ public class DataClient : AbstractClient { protected override void ShutdownEvent(StatusEvent e) { Logger.Debug("Clean shutdown detected from ziti"); - CleanShutdown = true; + ExpectedShutdown = true; base.ShutdownEvent(e); } @@ -89,7 +89,7 @@ protected virtual void MfaEvent(MfaEvent e) { protected virtual void NotificationEvent(NotificationEvent e) { OnNotificationEvent?.Invoke(this, e); - } + } protected virtual void ControllerEvent(ControllerEvent e) { OnControllerEvent?.Invoke(this, e); @@ -140,23 +140,15 @@ async protected override Task ConnectPipesAsync() { ClientConnected(null); } catch (Exception ex) { semaphoreSlim.Release(); - throw new ServiceException("Could not connect to the service.", 1, ex.Message); + throw new ServiceException("Could not connect to the data service.", 1, ex.Message); } semaphoreSlim.Release(); } async public Task GetStatusAsync() { - try { - await sendAsync(new ServiceFunction() { Command = "Status" }); - var rtn = await readAsync(ipcReader); - return rtn; - } catch (Exception ioe) { - //almost certainly a problem with the pipe - recreate the pipe... - //setupPipe(); - //throw ioe; - Logger.Error(ioe, "Unexpected error"); - } - return null; + await sendAsync(new ServiceFunction() { Command = "Status" }); + var rtn = await readAsync(ipcReader); + return rtn; } async public Task AddIdentityAsync(string jwtFileName, bool activate, string jwtContent) { @@ -326,6 +318,20 @@ async public Task RemoveMFA(string identifier, string totp) { return null; } + async public Task ExternalAuthLogin(string identifier) { + try { + await sendAsync(new ExternalAuthLogin(identifier)); + ExternalAuthLoginResponse extAuthResp = await readAsync(ipcReader); + return extAuthResp; + } catch (Exception ioe) { + //almost certainly a problem with the pipe - recreate the pipe... + //throw ioe; + Logger.Error(ioe, "Unexpected error"); + CommunicationError(ioe); + } + return null; + } + protected override void ProcessLine(string line) { try { string respAsString = line; @@ -348,7 +354,7 @@ protected override void ProcessLine(string line) { case "status": //break here to see status on startup //dbg comment Logger.Warn("STATUS EVENT: \n" + respAsString); TunnelStatusEvent tse = serializer.Deserialize(jsonReader); - + if (tse != null) { TunnelStatusEvent(tse); } diff --git a/ZitiDesktopEdge.Client/ServiceClient/MonitorClient.cs b/ZitiDesktopEdge.Client/ServiceClient/MonitorClient.cs index ca8df4239..88e0c24e5 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/MonitorClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/MonitorClient.cs @@ -1,19 +1,19 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + using System; using System.Collections.Generic; using System.IO; @@ -73,7 +73,7 @@ async protected override Task ConnectPipesAsync() { ClientConnected(null); } catch (Exception ex) { semaphoreSlim.Release(); - throw new ServiceException("Could not connect to the service.", 1, ex.Message); + throw new ServiceException("Could not connect to the monitor service.", 1, ex.Message); } semaphoreSlim.Release(); } @@ -92,38 +92,18 @@ protected override void ProcessLine(string line) { } } - async internal Task SendServiceFunctionAsync(object toSend) { - try { - await sendAsync(toSend); - var resp = await readMessageAsync(ipcReader); - Logger.Info("RESPONSE: {0}", resp); - return resp; - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; - } - async public Task StopServiceAsync() { ActionEvent action = new ActionEvent() { Op = "Stop", Action = "Normal" }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + await sendAsync(action); + return await readAsync(ipcReader); } + async public Task StartServiceAsync() { ActionEvent action = new ActionEvent() { Op = "Start", Action = "Normal" }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + await sendAsync(action); + return await readAsync(ipcReader); } + async public Task ForceTerminateAsync() { ActionEvent action = new ActionEvent() { Op = "Stop", Action = "Force" }; try { @@ -134,6 +114,7 @@ async public Task ForceTerminateAsync() { } return null; } + async public Task StatusAsync() { ActionEvent action = new ActionEvent() { Op = "Status", Action = "" }; try { @@ -144,82 +125,46 @@ async public Task StatusAsync() { } return null; } + async public Task CaptureLogsAsync() { ActionEvent action = new ActionEvent() { Op = "CaptureLogs", Action = "Normal" }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + await sendAsync(action); + return await readAsync(ipcReader); } - async public Task SetReleaseStreamAsync(string stream) { - ActionEvent action = new ActionEvent() { Op = "SetReleaseStream", Action = stream }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; - } async public Task SetLogLevelAsync(string level) { if ("verbose".Equals(level?.ToLower())) { //only the data client understands verbose - so use trace... level = "TRACE"; } ActionEvent action = new ActionEvent() { Op = "SetLogLevel", Action = level }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + await sendAsync(action); + return await readAsync(ipcReader); } + async public Task DoUpdateCheck() { ActionEvent action = new ActionEvent() { Op = "DoUpdateCheck", Action = "" }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + await sendAsync(action); + return await readAsync(ipcReader); } - async public Task TriggerUpdate() { - UpgradeSentinel.StartUpgradeSentinel(); - ActionEvent action = new ActionEvent() { Op = "TriggerUpdate", Action = "" }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + async public Task TriggerUpdate() { + UpgradeSentinel.StartUpgradeSentinel(); + ActionEvent action = new ActionEvent() { Op = "TriggerUpdate", Action = "" }; + await sendAsync(action); + return await readAsync(ipcReader); } + async public Task SetAutomaticUpgradeDisabledAsync(bool disabled) { ActionEvent action = new ActionEvent() { Op = "SetAutomaticUpgradeDisabled", Action = (disabled ? "true" : "false") }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + await sendAsync(action); + return await readAsync(ipcReader); } + async public Task SetAutomaticUpgradeURLAsync(string url) { ActionEvent action = new ActionEvent() { Op = "SetAutomaticUpgradeURL", Action = (url) }; - try { - await sendAsync(action); - return await readAsync(ipcReader); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error"); - } - return null; + await sendAsync(action); + return await readAsync(ipcReader); } } } \ No newline at end of file diff --git a/ZitiDesktopEdge.Client/Utility/GithubAPI.cs b/ZitiDesktopEdge.Client/Utility/GithubAPI.cs index 01d02a613..58f6a3144 100644 --- a/ZitiDesktopEdge.Client/Utility/GithubAPI.cs +++ b/ZitiDesktopEdge.Client/Utility/GithubAPI.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Net; using System.IO; @@ -23,37 +23,37 @@ limitations under the License. namespace ZitiDesktopEdge.Utility { public static class GithubAPI { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public const string ProdUrl = "https://get.openziti.io/zdew/stable.json"; - - public static JObject GetJson(string url) { - HttpWebRequest httpWebRequest = WebRequest.CreateHttp(url); - httpWebRequest.Method = "GET"; - httpWebRequest.ContentType = "application/json"; - httpWebRequest.UserAgent = "OpenZiti UpdateService"; - HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); - StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream()); - string currentResponse = streamReader.ReadToEnd(); - Logger.Trace("response received: {0}", currentResponse); - return JObject.Parse(currentResponse); - } - public static JArray GetJsonArray(string url) { - HttpWebRequest httpWebRequest = WebRequest.CreateHttp(url); - httpWebRequest.Method = "GET"; - httpWebRequest.ContentType = "application/json"; - httpWebRequest.UserAgent = "OpenZiti UpdateService"; - HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); - StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream()); - string currentResponse = streamReader.ReadToEnd(); - Logger.Trace("response received for url: {0}", url); - return JArray.Parse(currentResponse); - } - - public static Version GetVersion(JObject json) { - string releaseVersion = json.Property("tag_name").Value.ToString(); - string releaseName = json.Property("name").Value.ToString(); - return Version.Parse(releaseVersion); - } - } + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public const string ProdUrl = "https://get.openziti.io/zdew/stable.json"; + + public static JObject GetJson(string url) { + HttpWebRequest httpWebRequest = WebRequest.CreateHttp(url); + httpWebRequest.Method = "GET"; + httpWebRequest.ContentType = "application/json"; + httpWebRequest.UserAgent = "OpenZiti UpdateService"; + HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream()); + string currentResponse = streamReader.ReadToEnd(); + Logger.Trace("response received: {0}", currentResponse); + return JObject.Parse(currentResponse); + } + public static JArray GetJsonArray(string url) { + HttpWebRequest httpWebRequest = WebRequest.CreateHttp(url); + httpWebRequest.Method = "GET"; + httpWebRequest.ContentType = "application/json"; + httpWebRequest.UserAgent = "OpenZiti UpdateService"; + HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream()); + string currentResponse = streamReader.ReadToEnd(); + Logger.Trace("response received for url: {0}", url); + return JArray.Parse(currentResponse); + } + + public static Version GetVersion(JObject json) { + string releaseVersion = json.Property("tag_name").Value.ToString(); + string releaseName = json.Property("name").Value.ToString(); + return Version.Parse(releaseVersion); + } + } } diff --git a/ZitiDesktopEdge.Client/Utility/UpgradeSentinel.cs b/ZitiDesktopEdge.Client/Utility/UpgradeSentinel.cs index f1f7c4d2c..98616133f 100644 --- a/ZitiDesktopEdge.Client/Utility/UpgradeSentinel.cs +++ b/ZitiDesktopEdge.Client/Utility/UpgradeSentinel.cs @@ -25,49 +25,49 @@ limitations under the License. using System.Threading.Tasks; namespace ZitiDesktopEdge.Utility { - public class UpgradeSentinel { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - public const string ZitiUpgradeSentinelExeName = "ZitiUpgradeSentinel.exe"; - private static string SentinelTempSource = Path.Combine(Path.GetTempPath(), ZitiUpgradeSentinelExeName); + public class UpgradeSentinel { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + public const string ZitiUpgradeSentinelExeName = "ZitiUpgradeSentinel.exe"; + private static string SentinelTempSource = Path.Combine(Path.GetTempPath(), ZitiUpgradeSentinelExeName); - public static void StartUpgradeSentinel() { - //start the sentinel process... - using (Process process = new Process()) { - string executablePath = Assembly.GetEntryAssembly().Location; - string executableDirectory = Path.GetDirectoryName(executablePath); - var sentinelSource = Path.Combine(executableDirectory, ZitiUpgradeSentinelExeName); + public static void StartUpgradeSentinel() { + //start the sentinel process... + using (Process process = new Process()) { + string executablePath = Assembly.GetEntryAssembly().Location; + string executableDirectory = Path.GetDirectoryName(executablePath); + var sentinelSource = Path.Combine(executableDirectory, ZitiUpgradeSentinelExeName); - if (File.Exists(sentinelSource)) { - try { - File.Copy(sentinelSource, SentinelTempSource, true); - logger.Info("starting sentinel process: {}", SentinelTempSource); - process.StartInfo.FileName = SentinelTempSource; - process.StartInfo.Arguments = "version"; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - process.Start(); - } catch (Exception ex) { - logger.Error("cannot start sentinel service. {}", ex); - } - } else { - logger.Warn("cannot start sentinel service. source file doesn't exist? {}", sentinelSource); - } - } - } + if (File.Exists(sentinelSource)) { + try { + File.Copy(sentinelSource, SentinelTempSource, true); + logger.Info("starting sentinel process: {}", SentinelTempSource); + process.StartInfo.FileName = SentinelTempSource; + process.StartInfo.Arguments = "version"; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.Start(); + } catch (Exception ex) { + logger.Error("cannot start sentinel service. {}", ex); + } + } else { + logger.Warn("cannot start sentinel service. source file doesn't exist? {}", sentinelSource); + } + } + } - public static void RemoveUpgradeSentinelExe() { - try { - if (File.Exists(SentinelTempSource)) { - // if the temp file exists, clear it out - File.Delete(SentinelTempSource); - logger.Debug("found and removed upgrade sentinel at: {}", SentinelTempSource); - } else { - logger.Debug("no upgrade sentinel exe at {} found to remove", SentinelTempSource); - } - } catch (Exception ex) { - logger.Error($"OnStartup FAILED to delete the UpgradeSentinel at {SentinelTempSource}", ex); - } - } - } + public static void RemoveUpgradeSentinelExe() { + try { + if (File.Exists(SentinelTempSource)) { + // if the temp file exists, clear it out + File.Delete(SentinelTempSource); + logger.Debug("found and removed upgrade sentinel at: {}", SentinelTempSource); + } else { + logger.Debug("no upgrade sentinel exe at {} found to remove", SentinelTempSource); + } + } catch (Exception ex) { + logger.Error($"OnStartup FAILED to delete the UpgradeSentinel at {SentinelTempSource}", ex); + } + } + } } diff --git a/ZitiDesktopEdge.sln b/ZitiDesktopEdge.sln index 02ca30986..f6986126b 100644 --- a/ZitiDesktopEdge.sln +++ b/ZitiDesktopEdge.sln @@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Installer\build.ps1 = Installer\build.ps1 .github\workflows\installer.build.yml = .github\workflows\installer.build.yml .github\workflows\mattermost-ziti-webhook.yml = .github\workflows\mattermost-ziti-webhook.yml + Installer\output-build-json.ps1 = Installer\output-build-json.ps1 README.md = README.md Installer\reg.bat = Installer\reg.bat release-notes.md = release-notes.md @@ -32,6 +33,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "release-streams", "release-streams", "{36C30126-C9DC-4233-835E-9E036BCD1A29}" ProjectSection(SolutionItems) = preProject release-streams\beta.json = release-streams\beta.json + release-streams\ctrlha-alpha.json = release-streams\ctrlha-alpha.json release-streams\dev.json = release-streams\dev.json release-streams\latest.json = release-streams\latest.json release-streams\stable.json = release-streams\stable.json @@ -44,6 +46,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{472A EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{DB005713-0237-4719-9F7B-A239879B290D}" ProjectSection(SolutionItems) = preProject + .github\workflows\dependabot.yml = .github\workflows\dependabot.yml .github\workflows\installer.build.yml = .github\workflows\installer.build.yml .github\workflows\mattermost-ziti-webhook.yml = .github\workflows\mattermost-ziti-webhook.yml .github\workflows\release-drafter.yml = .github\workflows\release-drafter.yml diff --git a/ZitiUpdateService/Program.cs b/ZitiUpdateService/Program.cs index 0f2ceee63..5a8e97938 100644 --- a/ZitiUpdateService/Program.cs +++ b/ZitiUpdateService/Program.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.IO; using System.Reflection; using System.ServiceProcess; @@ -24,71 +24,71 @@ limitations under the License. namespace ZitiUpdateService { - static class Program { + static class Program { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - /// - /// The main entry point for the application. - /// - static void Main() { - var asm = Assembly.GetExecutingAssembly(); - var logname = asm.GetName().Name; + /// + /// The main entry point for the application. + /// + static void Main() { + var asm = Assembly.GetExecutingAssembly(); + var logname = asm.GetName().Name; - var curdir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - string nlogFile = Path.Combine(curdir, $"{logname}-log.config"); + var curdir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + string nlogFile = Path.Combine(curdir, $"{logname}-log.config"); - bool byFile = false; - if (File.Exists(nlogFile)) { - LogManager.Configuration = new XmlLoggingConfiguration(nlogFile); - byFile = true; - } else { - var config = new LoggingConfiguration(); - // Targets where to log to: File and Console - var logfile = new FileTarget("logfile") { - FileName = $"logs\\ZitiMonitorService\\{logname}.log", - ArchiveEvery = FileArchivePeriod.Day, - ArchiveNumbering = ArchiveNumberingMode.Rolling, - MaxArchiveFiles = 7, - AutoFlush = true, - Layout = "[${date:universalTime=true:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", - }; - var logconsole = new ConsoleTarget("logconsole"); + bool byFile = false; + if (File.Exists(nlogFile)) { + LogManager.Configuration = new XmlLoggingConfiguration(nlogFile); + byFile = true; + } else { + var config = new LoggingConfiguration(); + // Targets where to log to: File and Console + var logfile = new FileTarget("logfile") { + FileName = $"logs\\ZitiMonitorService\\{logname}.log", + ArchiveEvery = FileArchivePeriod.Day, + ArchiveNumbering = ArchiveNumberingMode.Rolling, + MaxArchiveFiles = 7, + AutoFlush = true, + Layout = "[${date:universalTime=true:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", + }; + var logconsole = new ConsoleTarget("logconsole"); - // Rules for mapping loggers to targets - config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); - config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); + // Rules for mapping loggers to targets + config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); + config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); - // Apply config - LogManager.Configuration = config; - } - Logger.Info("========================= ziti-monitor started ========================="); - Logger.Info("logger initialized"); - Logger.Info(" - version : {0}", asm.GetName().Version.ToString()); - Logger.Info(" - using file: {0}", byFile); - Logger.Info(" - file: {0}", nlogFile); - Logger.Info("========================================================================"); + // Apply config + LogManager.Configuration = config; + } + Logger.Info("========================= ziti-monitor started ========================="); + Logger.Info("logger initialized"); + Logger.Info(" - version : {0}", asm.GetName().Version.ToString()); + Logger.Info(" - using file: {0}", byFile); + Logger.Info(" - file: {0}", nlogFile); + Logger.Info("========================================================================"); UpdateService updateSvc = new UpdateService(); - updateSvc.AutoLog = true; - try { + updateSvc.AutoLog = true; + try { #if DEBUG - bool nosvc = true; - //bool nosvc = false; + bool nosvc = true; + //bool nosvc = false; - if (nosvc) { - Logger.Info(" - RUNNING AS DEBUG"); - updateSvc.Debug(); - updateSvc.WaitForCompletion(); - Logger.Info(" - RUNNING AS DEBUG COMPLETE"); - } else { - ServiceBase[] ServicesToRun = new ServiceBase[] - { - updateSvc - }; - Logger.Info("RUNNING AS DEBUG SERVICE"); - ServiceBase.Run(ServicesToRun); - } + if (nosvc) { + Logger.Info(" - RUNNING AS DEBUG"); + updateSvc.Debug(); + updateSvc.WaitForCompletion(); + Logger.Info(" - RUNNING AS DEBUG COMPLETE"); + } else { + ServiceBase[] ServicesToRun = new ServiceBase[] + { + updateSvc + }; + Logger.Info("RUNNING AS DEBUG SERVICE"); + ServiceBase.Run(ServicesToRun); + } #else ServiceBase[] ServicesToRun = new ServiceBase[] { @@ -96,9 +96,9 @@ static void Main() { }; ServiceBase.Run(ServicesToRun); #endif - } catch (Exception e) { - Logger.Error("Unexpected exception: {0}", e); - } + } catch (Exception e) { + Logger.Error("Unexpected exception: {0}", e); + } } } } diff --git a/ZitiUpdateService/ProjectInstaller.cs b/ZitiUpdateService/ProjectInstaller.cs index 98169c8b9..bd8ab8c45 100644 --- a/ZitiUpdateService/ProjectInstaller.cs +++ b/ZitiUpdateService/ProjectInstaller.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -23,14 +23,14 @@ limitations under the License. using System.Threading.Tasks; namespace ZitiUpdateService { - [RunInstaller(true)] - public partial class ProjectInstaller : System.Configuration.Install.Installer { - public ProjectInstaller() { - InitializeComponent(); - } + [RunInstaller(true)] + public partial class ProjectInstaller : System.Configuration.Install.Installer { + public ProjectInstaller() { + InitializeComponent(); + } - private void ZitiUpdateServiceInstaller_AfterInstall(object sender, InstallEventArgs e) { + private void ZitiUpdateServiceInstaller_AfterInstall(object sender, InstallEventArgs e) { - } - } + } + } } diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs index 369c0bfc8..c6a4a6edf 100644 --- a/ZitiUpdateService/Properties/AssemblyInfo.cs +++ b/ZitiUpdateService/Properties/AssemblyInfo.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System.Reflection; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index 9a675818d..fed183f08 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -1,19 +1,19 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + using System; using System.Diagnostics; using System.Linq; @@ -40,798 +40,778 @@ limitations under the License. using ZitiUpdateService.Utils; using ZitiUpdateService.Checkers; using System.Security.Policy; +using Newtonsoft.Json.Linq; +using System.Runtime.Remoting.Messaging; #if !SKIPUPDATE using ZitiUpdateService.Checkers.PeFile; #endif namespace ZitiUpdateService { - public partial class UpdateService : ServiceBase { - private string betaStreamMarkerFile = "use-beta-stream.txt"; - private static Settings CurrentSettings = new Settings(true); - - public bool IsBeta { - get { - return File.Exists(Path.Combine(exeLocation, betaStreamMarkerFile)); - } - private set { } - } - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - private System.Timers.Timer _updateTimer = new System.Timers.Timer(); - private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); - - private string exeLocation = null; - - private DataClient dataClient = new DataClient("monitor service"); - private bool running = false; - private string asmDir = null; - private string updateFolder = null; - private string filePrefix = "Ziti.Desktop.Edge.Client-"; - Version assemblyVersion = null; - - ServiceController controller; - IPCServer svr = new IPCServer(); - Task ipcServer = null; - Task eventServer = null; - - private System.Timers.Timer dnsProbeTimer = new System.Timers.Timer(); - private IPAddress dnsIpAddress = null; - - private UpdateCheck lastUpdateCheck; - private InstallationNotificationEvent lastInstallationNotification; - - public UpdateService() { - InitializeComponent(); - - CurrentSettings.Load(); - CurrentSettings.OnConfigurationChange += CurrentSettings_OnConfigurationChange; - - base.CanHandlePowerEvent = true; - - exeLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - - Logger.Info("Initializing"); - dataClient.OnClientConnected += Svc_OnClientConnected; - dataClient.OnTunnelStatusEvent += Svc_OnTunnelStatusEvent; - dataClient.OnClientDisconnected += Svc_OnClientDisconnected; - dataClient.OnShutdownEvent += Svc_OnShutdownEvent; - dataClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; - dataClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; - - svr.CaptureLogs = CaptureLogs; - svr.SetLogLevel = SetLogLevel; - svr.SetReleaseStream = SetReleaseStream; - svr.DoUpdateCheck = DoUpdateCheck; - svr.TriggerUpdate = TriggerUpdate; - svr.SetAutomaticUpdateDisabled = SetAutomaticUpdateDisabled; - svr.SetAutomaticUpdateURL = SetAutomaticUpdateURL; - - string assemblyVersionStr = Assembly.GetExecutingAssembly().GetName().Version.ToString(); //fetch from ziti? - assemblyVersion = new Version(assemblyVersionStr); - asmDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - updateFolder = Path.Combine(asmDir, "updates"); - if (!Directory.Exists(updateFolder)) { - Directory.CreateDirectory(updateFolder); - } - } - - private SvcResponse SetAutomaticUpdateURL(string url) { - SvcResponse failure = new SvcResponse(); - failure.Code = (int)ErrorCodes.URL_INVALID; - failure.Error = $"The url supplied is invalid: \n{url}\n"; - failure.Message = "Failure"; - - - SvcResponse r = new SvcResponse(); - if (url == null || !url.StartsWith("http")) { - return failure; - } else { - // check the url exists and appears correct... - var check = new GithubCheck(assemblyVersion, url); - - if (check != null) { - var v = check.GetNextVersion(); - - if (v == null) { - return failure; - } - if (v.Revision.ToString().Trim() == "") { - return failure; - } - } - - checkUpdateImmediately(); - - CurrentSettings.AutomaticUpdateURL = url; - CurrentSettings.Write(); - r.Message = "Success"; - } - return r; - } - - private void CurrentSettings_OnConfigurationChange(object sender, ControllerEvent e) { - MonitorServiceStatusEvent evt; - if (lastInstallationNotification != null) { - evt = lastInstallationNotification; - } else { - evt = new MonitorServiceStatusEvent() { - Code = 0, - Error = "", - Message = "Configuration Changed", - Type = "Status", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - }; - } - Logger.Debug($"notifying consumers of change to CurrentSettings. AutomaticUpdates status = {(CurrentSettings.AutomaticUpdatesDisabled ? "disabled" : "enabled")}"); - EventRegistry.SendEventToConsumers(evt); - } - - private SvcResponse SetAutomaticUpdateDisabled(bool disabled) { - if (lastInstallationNotification != null) { - lastInstallationNotification.AutomaticUpgradeDisabled = disabled.ToString(); - } - CurrentSettings.AutomaticUpdatesDisabled = disabled; - CurrentSettings.Write(); - SvcResponse r = new SvcResponse(); - r.Message = "Success"; - return r; - } - - private SvcResponse TriggerUpdate() { - SvcResponse r = new SvcResponse(); - r.Message = "Initiating Update"; - - Task.Run(() => { installZDE(lastUpdateCheck); }); - return r; - } - - - private void checkUpdateImmediately() { - try { - CheckUpdate(null, null); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in CheckUpdate"); - } - } - - private StatusCheck DoUpdateCheck() { - StatusCheck r = new StatusCheck(); - - UpdateCheck check = getCheck(assemblyVersion); - - r.Code = check.Avail; - r.ReleaseStream = IsBeta ? "beta" : "stable"; - switch (r.Code) { - case -1: - r.Message = $"An update is available: {check.GetNextVersion()}"; - r.UpdateAvailable = true; - Logger.Debug("Update {0} is published on {1}", check.GetNextVersion(), check.PublishDate); - checkUpdateImmediately(); - break; - case 0: - r.Message = $"The current version [{assemblyVersion}] is the latest"; - break; - case 1: - r.Message = $"Your version [{assemblyVersion}] is newer than the latest release"; - break; - default: - r.Message = "Update check failed"; - break; - } - return r; - } - - private void SetLogLevel(string level) { - try { - Logger.Info("request to change log level received: {0}", level); - if (("" + level).ToLower().Trim() == "verbose") { - level = "trace"; - Logger.Info("request to change log level to verbose - but using trace instead"); - } - var l = LogLevel.FromString(level); - foreach (var rule in LogManager.Configuration.LoggingRules) { - rule.EnableLoggingForLevel(l); - rule.SetLoggingLevels(l, LogLevel.Fatal); - } - - LogManager.ReconfigExistingLoggers(); - Logger.Info("logger reconfigured to log at level: {0}", l); - } catch (Exception e) { - Logger.Error(e, "Could NOT set the log level for loggers??? {0}", e.Message); - } - } - - private void SetReleaseStream(string stream) { - string markerFile = Path.Combine(exeLocation, betaStreamMarkerFile); - if (stream == "beta") { - if (IsBeta) { - Logger.Debug("already using beta stream. No action taken"); - } else { - Logger.Info("Setting update service to use beta stream!"); - using (File.Create(markerFile)) { - - } - AccessUtils.GrantAccessToFile(markerFile); //allow anyone to delete this manually if need be... - Logger.Debug("added marker file: {0}", markerFile); - } - } else { - if (!IsBeta) { - Logger.Debug("already using release stream. No action taken"); - } else { - Logger.Info("Setting update service to use release stream!"); - if (File.Exists(markerFile)) { - File.Delete(markerFile); - Logger.Debug("removed marker file: {0}", markerFile); - } - } - } - } - - private string CaptureLogs() { - try { - string logLocation = Path.Combine(exeLocation, "logs"); - string destinationLocation = Path.Combine(exeLocation, "temp"); - string serviceLogsLocation = Path.Combine(logLocation, "service"); - string serviceLogsDest = Path.Combine(destinationLocation, "service"); - - Logger.Debug("removing leftover temp folder: {0}", destinationLocation); - try { - Directory.Delete(destinationLocation, true); - } catch { - //means it doesn't exist - } - - Directory.CreateDirectory(destinationLocation); - - Logger.Debug("copying all directories from: {0}", logLocation); - foreach (string dirPath in Directory.GetDirectories(logLocation, "*", SearchOption.AllDirectories)) { - Directory.CreateDirectory(dirPath.Replace(logLocation, destinationLocation)); - } - - Logger.Debug("copying all non-zip files from: {0}", logLocation); - foreach (string newPath in Directory.GetFiles(logLocation, "*.*", SearchOption.AllDirectories)) { - if (!newPath.EndsWith(".zip")) { - File.Copy(newPath, newPath.Replace(logLocation, destinationLocation), true); - } - } - - Logger.Debug("copying service files from: {0} to {1}", serviceLogsLocation, serviceLogsDest); - Directory.CreateDirectory(serviceLogsDest); - foreach (string newPath in Directory.GetFiles(serviceLogsLocation, "*.*", SearchOption.TopDirectoryOnly)) { - if (newPath.EndsWith(".log") || newPath.Contains("config.json")) { - Logger.Debug("copying service log: {0}", newPath); - File.Copy(newPath, newPath.Replace(serviceLogsLocation, serviceLogsDest), true); - } - } - - outputIpconfigInfo(destinationLocation); - outputSystemInfo(destinationLocation); - outputDnsCache(destinationLocation); - outputExternalIP(destinationLocation); - outputTasklist(destinationLocation); - outputRouteInfo(destinationLocation); - outputNetstatInfo(destinationLocation); - outputNrpt(destinationLocation); - - Task.Delay(500).Wait(); - - string zipName = Path.Combine(logLocation, DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".zip"); - ZipFile.CreateFromDirectory(destinationLocation, zipName); - - Logger.Debug("cleaning up temp folder: {0}", destinationLocation); - try { - Directory.Delete(destinationLocation, true); - } catch { - //means it doesn't exist - } - return zipName; - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in generating system files {0}", ex.Message); - return null; - } - } - - private void outputIpconfigInfo(string destinationFolder) { - Logger.Info("capturing ipconfig information"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var ipconfigOut = Path.Combine(destinationFolder, "ipconfig.all.txt"); - Logger.Debug("copying ipconfig /all to {0}", ipconfigOut); - startInfo.Arguments = $"/C ipconfig /all > \"{ipconfigOut}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputIpconfigInfo {0}", ex.Message); - } - } - - private void outputSystemInfo(string destinationFolder) { - Logger.Info("capturing systeminfo"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var sysinfoOut = Path.Combine(destinationFolder, "systeminfo.txt"); - Logger.Debug("running systeminfo to {0}", sysinfoOut); - startInfo.Arguments = $"/C systeminfo > \"{sysinfoOut}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputSystemInfo {0}", ex.Message); - } - } - - private void outputDnsCache(string destinationFolder) { - Logger.Info("capturing dns cache information"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var dnsCache = Path.Combine(destinationFolder, "dnsCache.txt"); - Logger.Debug("running ipconfig /displaydns to {0}", dnsCache); - startInfo.Arguments = $"/C ipconfig /displaydns > \"{dnsCache}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputDnsCache {0}", ex.Message); - } - } - - private void outputExternalIP(string destinationFolder) { - Logger.Info("capturing external IP address using nslookup command"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var extIpFile = Path.Combine(destinationFolder, "externalIP.txt"); - Logger.Debug("running nslookup myip.opendns.com. resolver1.opendns.com to {0}", extIpFile); - startInfo.Arguments = $"/C nslookup myip.opendns.com. resolver1.opendns.com > \"{extIpFile}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputExternalIP {0}", ex.Message); - } - } - - private void outputTasklist(string destinationFolder) { - Logger.Info("capturing executing tasks"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var tasklistOutput = Path.Combine(destinationFolder, "tasklist.txt"); - Logger.Debug("running tasklist to {0}", tasklistOutput); - startInfo.Arguments = $"/C tasklist > \"{tasklistOutput}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - private void outputRouteInfo(string destinationFolder) { - Logger.Info("capturing network routes"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var networkRoutes = Path.Combine(destinationFolder, "network-routes.txt"); - Logger.Debug("running route print to {0}", networkRoutes); - startInfo.Arguments = $"/C route print > \"{networkRoutes}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - private void outputNetstatInfo(string destinationFolder) { - Logger.Info("capturing netstat"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var netstatOutput = Path.Combine(destinationFolder, "netstat.txt"); - Logger.Debug("running netstat -ano to {0}", netstatOutput); - startInfo.Arguments = $"/C netstat -ano > \"{netstatOutput}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - private void outputNrpt(string destinationFolder) { - Logger.Info("outputting NRPT rules"); - try { - Logger.Info("outputting NRPT DnsClientNrptRule"); - string nrptRuleOutput = Path.Combine(destinationFolder, "NrptRule.txt"); - Process nrptRuleProcess = new Process(); - ProcessStartInfo nrptRuleStartInfo = new ProcessStartInfo(); - nrptRuleStartInfo.WindowStyle = ProcessWindowStyle.Hidden; - nrptRuleStartInfo.FileName = "cmd.exe"; - nrptRuleStartInfo.Arguments = $"/C powershell \"Get-DnsClientNrptRule | sort -Property Namespace\" > \"{nrptRuleOutput}\""; - Logger.Info("Running: {0}", nrptRuleStartInfo.Arguments); - nrptRuleProcess.StartInfo = nrptRuleStartInfo; - nrptRuleProcess.Start(); - nrptRuleProcess.WaitForExit(); - - Logger.Info("outputting NRPT DnsClientNrptPolicy"); - string nrptOutput = Path.Combine(destinationFolder, "NrptPolicy.txt"); - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - startInfo.Arguments = $"/C powershell \"Get-DnsClientNrptPolicy | sort -Property Namespace\" > \"{nrptOutput}\""; - Logger.Info("Running: {0}", startInfo.Arguments); - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - public void Debug() { - OnStart(null);// new string[] { "FilesystemCheck" }); - } - - protected override void OnStart(string[] args) { - Logger.Debug("args: {0}", args); - Logger.Info("ziti-monitor service is starting"); - - var logs = Path.Combine(exeLocation, "logs"); - addLogsFolder(exeLocation); - addLogsFolder(logs); - addLogsFolder(Path.Combine(logs, "UI")); - addLogsFolder(Path.Combine(logs, "ZitiMonitorService")); - addLogsFolder(Path.Combine(logs, "service")); - - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService.exe.config")); //allow anyone to change the config file - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService-log.config")); //allow anyone to change the log file config - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge.exe.config")); //allow anyone to change the config file - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge-log.config")); //allow anyone to change the log file config - - //dnsProbeTimer.Elapsed += DnsProbe_Elapsed; disable the dns probe for now - dnsProbeTimer.Interval = dnsProbeIntervalInSeconds * 1000; - - Logger.Info("starting ipc server"); - ipcServer = svr.startIpcServerAsync(onIpcClientAsync); - Logger.Info("starting events server"); - eventServer = svr.startEventsServerAsync(onEventsClientAsync); - - Logger.Info("starting service watchers"); - if (!running) { - running = true; - Task.Run(() => { - SetupServiceWatchers(); - }); - } - Logger.Info("ziti-monitor service is initialized and running"); - base.OnStart(args); - } - - IPAddress lh = IPAddress.Parse("127.0.0.1"); //expected result - int dnsProbeFailCount = 0; - int dnsProbeIntervalInSeconds = 60; - bool dnsProbeStarted = false; - - private void DnsProbe_Elapsed(object sender, ElapsedEventArgs e) { - if (dnsProbeStarted) return; //skip out if it's already going... - dnsProbeStarted = true; - Logger.Trace("dns probe started"); - try { - if (dnsIpAddress != null) { - DnsQuestion q = new DnsQuestion("dew-dns-probe.openziti.org", QueryType.A); - var dnsEp = new IPEndPoint(dnsIpAddress, 53); - var dnsProbe = new LookupClient(dnsEp); - IDnsQueryResponse resp = dnsProbe.Query(q); - if (resp != null && resp.Answers?.Count > 0) { - foreach (DnsResourceRecord dnsrec in resp.Answers) { - if (dnsrec.GetType() == typeof(ARecord)) { - ARecord arec = (ARecord)dnsrec; - if (arec.Address.Equals(lh)) { - dnsProbeFailCount = 0; - Logger.Debug("dns probe success"); - } else { - logDnsProbeFailure(null); - } - } else { - Logger.Debug("dns probe returned an answer but response was not an ARecord? [" + dnsrec.GetType().Name + "]"); - } - } - } else { - Logger.Debug("dns probe failed. no answer found for dew-dns-probe.openziti.org"); - logDnsProbeFailure(null); - } - } - } catch (Exception dnse) { - //don't really care but it probably means a timeout happened. but might as well log a trace error anyway... - //it's expected that this is due to the service shutting down... - logDnsProbeFailure(dnse); - } - dnsProbeStarted = false; - } - - private void logDnsProbeFailure(Exception e) { - dnsProbeFailCount++; - bool logit = false; - if (dnsProbeFailCount <= 4) { - logit = true; - } else { - //else log it every 5 minutes... - logit = dnsProbeFailCount % (5 * 60 / dnsProbeIntervalInSeconds) == 0; - } - if (logit) { - if (e != null) { - Logger.Warn(e, "dns probe failed due to error. This has happened {0} times", dnsProbeFailCount); - } else { - Logger.Warn("dns probe failed. This has happened {0} times", dnsProbeFailCount); - } - } - } - - async private Task onEventsClientAsync(StreamWriter writer) { - Logger.Info("a new events client was connected"); - //reset to release stream - //initial status when connecting the event stream - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 0, - Error = "", - Message = "Success", - Type = "Status", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - }; - await writer.WriteLineAsync(JsonConvert.SerializeObject(status)); - await writer.FlushAsync(); - - //if a new client attaches - send the last update check status - if (lastUpdateCheck != null) { - await writer.WriteLineAsync(JsonConvert.SerializeObject(lastInstallationNotification)); - await writer.FlushAsync(); - } - } + public partial class UpdateService : ServiceBase { + private const string betaStreamMarkerFile = "use-beta-stream.txt"; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static Settings CurrentSettings = new Settings(true); + + public bool IsBeta { + get { + return File.Exists(Path.Combine(exeLocation, betaStreamMarkerFile)); + } + private set { } + } + + + private System.Timers.Timer _updateTimer = new System.Timers.Timer(); + private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); + + private string exeLocation = null; + + private DataClient dataClient = new DataClient("monitor service"); + private bool running = false; + private string asmDir = null; + private string updateFolder = null; + private string filePrefix = "Ziti.Desktop.Edge.Client-"; + private Version assemblyVersion = null; + + private ServiceController controller; + private IPCServer svr = new IPCServer(); + private Task ipcServer = null; + private Task eventServer = null; + + private const int zetHealthcheckInterval = 5; + private SemaphoreSlim zetSemaphore = new SemaphoreSlim(1, 1); + private System.Timers.Timer zetHealthcheck = new System.Timers.Timer(); + private int zetFailedCheckCounter = 0; + + private UpdateCheck lastUpdateCheck; + private InstallationNotificationEvent lastInstallationNotification; + + public UpdateService() { + InitializeComponent(); + + CurrentSettings.Load(); + CurrentSettings.OnConfigurationChange += CurrentSettings_OnConfigurationChange; + + base.CanHandlePowerEvent = true; + + exeLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + Logger.Info("Initializing"); + dataClient.OnClientConnected += Svc_OnClientConnected; + dataClient.OnTunnelStatusEvent += Svc_OnTunnelStatusEvent; + dataClient.OnClientDisconnected += Svc_OnClientDisconnected; + dataClient.OnShutdownEvent += Svc_OnShutdownEvent; + dataClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; + dataClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; + + svr.CaptureLogs = CaptureLogs; + svr.SetLogLevel = SetLogLevel; + svr.SetReleaseStream = SetReleaseStream; + svr.DoUpdateCheck = DoUpdateCheck; + svr.TriggerUpdate = TriggerUpdate; + svr.SetAutomaticUpdateDisabled = SetAutomaticUpdateDisabled; + svr.SetAutomaticUpdateURL = SetAutomaticUpdateURL; + + string assemblyVersionStr = Assembly.GetExecutingAssembly().GetName().Version.ToString(); //fetch from ziti? + assemblyVersion = new Version(assemblyVersionStr); + asmDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + updateFolder = Path.Combine(asmDir, "updates"); + if (!Directory.Exists(updateFolder)) { + Directory.CreateDirectory(updateFolder); + } + } + + private SvcResponse SetAutomaticUpdateURL(string url) { + SvcResponse failure = new SvcResponse(); + failure.Code = (int)ErrorCodes.URL_INVALID; + failure.Error = $"The url supplied is invalid: \n{url}\n"; + failure.Message = "Failure"; + + + SvcResponse r = new SvcResponse(); + if (url == null || !url.StartsWith("http")) { + return failure; + } else { + // check the url exists and appears correct... + var check = new GithubCheck(assemblyVersion, url); + + if (check != null) { + var v = check.GetNextVersion(); + + if (v == null) { + return failure; + } + if (v.Revision.ToString().Trim() == "") { + return failure; + } + } + + checkUpdateImmediately(); + + CurrentSettings.AutomaticUpdateURL = url; + CurrentSettings.Write(); + r.Message = "Success"; + } + return r; + } + + private void CurrentSettings_OnConfigurationChange(object sender, ControllerEvent e) { + MonitorServiceStatusEvent evt; + if (lastInstallationNotification != null) { + evt = lastInstallationNotification; + } else { + evt = new MonitorServiceStatusEvent() { + Code = 0, + Error = "", + Message = "Configuration Changed", + Type = "Status", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + }; + } + Logger.Debug($"notifying consumers of change to CurrentSettings. AutomaticUpdates status = {(CurrentSettings.AutomaticUpdatesDisabled ? "disabled" : "enabled")}"); + EventRegistry.SendEventToConsumers(evt); + } + + private SvcResponse SetAutomaticUpdateDisabled(bool disabled) { + if (lastInstallationNotification != null) { + lastInstallationNotification.AutomaticUpgradeDisabled = disabled.ToString(); + } + CurrentSettings.AutomaticUpdatesDisabled = disabled; + CurrentSettings.Write(); + SvcResponse r = new SvcResponse(); + r.Message = "Success"; + return r; + } + + private SvcResponse TriggerUpdate() { + SvcResponse r = new SvcResponse(); + r.Message = "Initiating Update"; + + Task.Run(() => { installZDE(lastUpdateCheck); }); + return r; + } + + + private void checkUpdateImmediately() { + try { + CheckUpdate(null, null); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in CheckUpdate"); + } + } + + private StatusCheck DoUpdateCheck() { + StatusCheck r = new StatusCheck(); + + UpdateCheck check = getCheck(assemblyVersion); + + r.Code = check.Avail; + r.ReleaseStream = IsBeta ? "beta" : "stable"; + switch (r.Code) { + case -1: + r.Message = $"An update is available: {check.GetNextVersion()}"; + r.UpdateAvailable = true; + Logger.Debug("Update {0} is published on {1}", check.GetNextVersion(), check.PublishDate); + checkUpdateImmediately(); + break; + case 0: + r.Message = $"The current version [{assemblyVersion}] is the latest"; + break; + case 1: + r.Message = $"Your version [{assemblyVersion}] is newer than the latest release"; + break; + default: + r.Message = "Update check failed"; + break; + } + return r; + } + + private void SetLogLevel(string level) { + try { + Logger.Info("request to change log level received: {0}", level); + if (("" + level).ToLower().Trim() == "verbose") { + level = "trace"; + Logger.Info("request to change log level to verbose - but using trace instead"); + } + var l = LogLevel.FromString(level); + foreach (var rule in LogManager.Configuration.LoggingRules) { + rule.EnableLoggingForLevel(l); + rule.SetLoggingLevels(l, LogLevel.Fatal); + } + + LogManager.ReconfigExistingLoggers(); + Logger.Info("logger reconfigured to log at level: {0}", l); + } catch (Exception e) { + Logger.Error(e, "Could NOT set the log level for loggers??? {0}", e.Message); + } + } + + private void SetReleaseStream(string stream) { + string markerFile = Path.Combine(exeLocation, betaStreamMarkerFile); + if (stream == "beta") { + if (IsBeta) { + Logger.Debug("already using beta stream. No action taken"); + } else { + Logger.Info("Setting update service to use beta stream!"); + using (File.Create(markerFile)) { + + } + AccessUtils.GrantAccessToFile(markerFile); //allow anyone to delete this manually if need be... + Logger.Debug("added marker file: {0}", markerFile); + } + } else { + if (!IsBeta) { + Logger.Debug("already using release stream. No action taken"); + } else { + Logger.Info("Setting update service to use release stream!"); + if (File.Exists(markerFile)) { + File.Delete(markerFile); + Logger.Debug("removed marker file: {0}", markerFile); + } + } + } + } + + private string CaptureLogs() { + try { + string logLocation = Path.Combine(exeLocation, "logs"); + string destinationLocation = Path.Combine(exeLocation, "temp"); + string serviceLogsLocation = Path.Combine(logLocation, "service"); + string serviceLogsDest = Path.Combine(destinationLocation, "service"); + + Logger.Debug("removing leftover temp folder: {0}", destinationLocation); + try { + Directory.Delete(destinationLocation, true); + } catch { + //means it doesn't exist + } + + Directory.CreateDirectory(destinationLocation); + + Logger.Debug("copying all directories from: {0}", logLocation); + foreach (string dirPath in Directory.GetDirectories(logLocation, "*", SearchOption.AllDirectories)) { + Directory.CreateDirectory(dirPath.Replace(logLocation, destinationLocation)); + } + + Logger.Debug("copying all non-zip files from: {0}", logLocation); + foreach (string newPath in Directory.GetFiles(logLocation, "*.*", SearchOption.AllDirectories)) { + if (!newPath.EndsWith(".zip")) { + File.Copy(newPath, newPath.Replace(logLocation, destinationLocation), true); + } + } + + Logger.Debug("copying service files from: {0} to {1}", serviceLogsLocation, serviceLogsDest); + Directory.CreateDirectory(serviceLogsDest); + foreach (string newPath in Directory.GetFiles(serviceLogsLocation, "*.*", SearchOption.TopDirectoryOnly)) { + if (newPath.EndsWith(".log") || newPath.Contains("config.json")) { + Logger.Debug("copying service log: {0}", newPath); + File.Copy(newPath, newPath.Replace(serviceLogsLocation, serviceLogsDest), true); + } + } + + outputIpconfigInfo(destinationLocation); + outputSystemInfo(destinationLocation); + outputDnsCache(destinationLocation); + outputExternalIP(destinationLocation); + outputTasklist(destinationLocation); + outputRouteInfo(destinationLocation); + outputNetstatInfo(destinationLocation); + outputNrpt(destinationLocation); + + Task.Delay(500).Wait(); + + string zipName = Path.Combine(logLocation, DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".zip"); + ZipFile.CreateFromDirectory(destinationLocation, zipName); + + Logger.Debug("cleaning up temp folder: {0}", destinationLocation); + try { + Directory.Delete(destinationLocation, true); + } catch { + //means it doesn't exist + } + return zipName; + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in generating system files {0}", ex.Message); + return null; + } + } + + private void outputIpconfigInfo(string destinationFolder) { + Logger.Info("capturing ipconfig information"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var ipconfigOut = Path.Combine(destinationFolder, "ipconfig.all.txt"); + Logger.Debug("copying ipconfig /all to {0}", ipconfigOut); + startInfo.Arguments = $"/C ipconfig /all > \"{ipconfigOut}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputIpconfigInfo {0}", ex.Message); + } + } + + private void outputSystemInfo(string destinationFolder) { + Logger.Info("capturing systeminfo"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var sysinfoOut = Path.Combine(destinationFolder, "systeminfo.txt"); + Logger.Debug("running systeminfo to {0}", sysinfoOut); + startInfo.Arguments = $"/C systeminfo > \"{sysinfoOut}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputSystemInfo {0}", ex.Message); + } + } + + private void outputDnsCache(string destinationFolder) { + Logger.Info("capturing dns cache information"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var dnsCache = Path.Combine(destinationFolder, "dnsCache.txt"); + Logger.Debug("running ipconfig /displaydns to {0}", dnsCache); + startInfo.Arguments = $"/C ipconfig /displaydns > \"{dnsCache}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputDnsCache {0}", ex.Message); + } + } + + private void outputExternalIP(string destinationFolder) { + Logger.Info("capturing external IP address using nslookup command"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var extIpFile = Path.Combine(destinationFolder, "externalIP.txt"); + Logger.Debug("running nslookup myip.opendns.com. resolver1.opendns.com to {0}", extIpFile); + startInfo.Arguments = $"/C nslookup myip.opendns.com. resolver1.opendns.com > \"{extIpFile}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputExternalIP {0}", ex.Message); + } + } + + private void outputTasklist(string destinationFolder) { + Logger.Info("capturing executing tasks"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var tasklistOutput = Path.Combine(destinationFolder, "tasklist.txt"); + Logger.Debug("running tasklist to {0}", tasklistOutput); + startInfo.Arguments = $"/C tasklist > \"{tasklistOutput}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + private void outputRouteInfo(string destinationFolder) { + Logger.Info("capturing network routes"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var networkRoutes = Path.Combine(destinationFolder, "network-routes.txt"); + Logger.Debug("running route print to {0}", networkRoutes); + startInfo.Arguments = $"/C route print > \"{networkRoutes}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + private void outputNetstatInfo(string destinationFolder) { + Logger.Info("capturing netstat"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var netstatOutput = Path.Combine(destinationFolder, "netstat.txt"); + Logger.Debug("running netstat -ano to {0}", netstatOutput); + startInfo.Arguments = $"/C netstat -ano > \"{netstatOutput}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + private void outputNrpt(string destinationFolder) { + Logger.Info("outputting NRPT rules"); + try { + Logger.Info("outputting NRPT DnsClientNrptRule"); + string nrptRuleOutput = Path.Combine(destinationFolder, "NrptRule.txt"); + Process nrptRuleProcess = new Process(); + ProcessStartInfo nrptRuleStartInfo = new ProcessStartInfo(); + nrptRuleStartInfo.WindowStyle = ProcessWindowStyle.Hidden; + nrptRuleStartInfo.FileName = "cmd.exe"; + nrptRuleStartInfo.Arguments = $"/C powershell \"Get-DnsClientNrptRule | sort -Property Namespace\" > \"{nrptRuleOutput}\""; + Logger.Info("Running: {0}", nrptRuleStartInfo.Arguments); + nrptRuleProcess.StartInfo = nrptRuleStartInfo; + nrptRuleProcess.Start(); + nrptRuleProcess.WaitForExit(); + + Logger.Info("outputting NRPT DnsClientNrptPolicy"); + string nrptOutput = Path.Combine(destinationFolder, "NrptPolicy.txt"); + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + startInfo.Arguments = $"/C powershell \"Get-DnsClientNrptPolicy | sort -Property Namespace\" > \"{nrptOutput}\""; + Logger.Info("Running: {0}", startInfo.Arguments); + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + public void Debug() { + OnStart(null);// new string[] { "FilesystemCheck" }); + } + + protected override void OnStart(string[] args) { + Logger.Debug("args: {0}", args); + Logger.Info("ziti-monitor service is starting"); + + var logs = Path.Combine(exeLocation, "logs"); + addLogsFolder(exeLocation); + addLogsFolder(logs); + addLogsFolder(Path.Combine(logs, "UI")); + addLogsFolder(Path.Combine(logs, "ZitiMonitorService")); + addLogsFolder(Path.Combine(logs, "service")); + + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService.exe.config")); //allow anyone to change the config file + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService-log.config")); //allow anyone to change the log file config + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge.exe.config")); //allow anyone to change the config file + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge-log.config")); //allow anyone to change the log file config + + zetHealthcheck.Interval = zetHealthcheckInterval * 1000; + zetHealthcheck.Elapsed += zitiEdgeTunnelAlivenessCheck; + + Logger.Info("starting ipc server"); + ipcServer = svr.startIpcServerAsync(onIpcClientAsync); + Logger.Info("starting events server"); + eventServer = svr.startEventsServerAsync(onEventsClientAsync); + + Logger.Info("starting service watchers"); + if (!running) { + running = true; + Task.Run(() => { + SetupServiceWatchers(); + }); + } + Logger.Info("ziti-monitor service is initialized and running"); + base.OnStart(args); + } + + private void zitiEdgeTunnelAlivenessCheck(object sender, ElapsedEventArgs e) { + try { + if (zetSemaphore.Wait(TimeSpan.FromSeconds(zetHealthcheckInterval))) { + Logger.Trace("ziti-edge-tunnel aliveness check starts"); + dataClient.GetStatusAsync().Wait(); + zetSemaphore.Release(); + Interlocked.Exchange(ref zetFailedCheckCounter, 0); + Logger.Trace("ziti-edge-tunnel aliveness check ends successfully"); + } else { + Interlocked.Add(ref zetFailedCheckCounter, 1); + Logger.Warn("ziti-edge-tunnel aliveness check appears blocked and has been for {} times", zetFailedCheckCounter); + if (zetFailedCheckCounter > 2) { + disableHealthCheck(); + //after 3 failures, just terminate ziti-edge-tunnel + Interlocked.Exchange(ref zetFailedCheckCounter, 0); //reset the counter back to 0 + Logger.Warn("forcefully stopping ziti-edge-tunnel as it has been blocked for too long"); + stopProcessForcefully("ziti-edge-tunnel", "data service [ziti]"); + + Logger.Info("immediately restarting ziti-edge-tunnel"); + ServiceActions.StartService(); //attempt to start the service + } + } + } catch (Exception ex) { + Logger.Error("ziti-edge-tunnel aliveness check ends exceptionally: {}", ex.Message); + Logger.Error(ex); + } + } + + async private Task onEventsClientAsync(StreamWriter writer) { + try { + Logger.Info("a new events client was connected"); + //reset to release stream + //initial status when connecting the event stream + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 0, + Error = "", + Message = "Success", + Type = "Status", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + }; + await writer.WriteLineAsync(JsonConvert.SerializeObject(status)); + await writer.FlushAsync(); + + //if a new client attaches - send the last update check status + if (lastUpdateCheck != null) { + await writer.WriteLineAsync(JsonConvert.SerializeObject(lastInstallationNotification)); + await writer.FlushAsync(); + } + } catch (Exception ex) { + Logger.Error("UNEXPECTED ERROR: {}", ex); + } + } #pragma warning disable 1998 //This async method lacks 'await' - async private Task onIpcClientAsync(StreamWriter writer) { - Logger.Info("a new ipc client was connected"); - } + async private Task onIpcClientAsync(StreamWriter writer) { + Logger.Info("a new ipc client was connected"); + } #pragma warning restore 1998 //This async method lacks 'await' - private void addLogsFolder(string path) { - if (!Directory.Exists(path)) { - Logger.Info($"creating folder: {path}"); - Directory.CreateDirectory(path); - AccessUtils.GrantAccessToDirectory(path); - } - } - - public void WaitForCompletion() { - Task.WaitAll(ipcServer, eventServer); - } - - protected override void OnStop() { - Logger.Info("ziti-monitor OnStop was called"); - base.OnStop(); - } - - protected override void OnPause() { - Logger.Info("ziti-monitor OnPause was called"); - base.OnPause(); - } - - protected override void OnShutdown() { - Logger.Info("ziti-monitor OnShutdown was called"); - base.OnShutdown(); - } - - protected override void OnContinue() { - Logger.Info("ziti-monitor OnContinue was called"); - base.OnContinue(); - } - - protected override void OnCustomCommand(int command) { - Logger.Info("ziti-monitor OnCustomCommand was called {0}", command); - base.OnCustomCommand(command); - } - - protected override void OnSessionChange(SessionChangeDescription changeDescription) { - Logger.Info("ziti-monitor OnSessionChange was called {0}", changeDescription); - base.OnSessionChange(changeDescription); - } - - protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) { - Logger.Info("ziti-monitor OnPowerEvent was called {0}", powerStatus); - return base.OnPowerEvent(powerStatus); - } - - private void SetupServiceWatchers() { - var updateTimerInterval = ConfigurationManager.AppSettings.Get("UpdateTimer"); - var upInt = TimeSpan.Zero; - if (!TimeSpan.TryParse(updateTimerInterval, out upInt)) { - upInt = new TimeSpan(0, 10, 0); - } - - if (upInt.TotalMilliseconds < 10 * 60 * 1000) { - Logger.Warn("provided time [{0}] is too small. Using 10 minutes.", updateTimerInterval); + private void addLogsFolder(string path) { + if (!Directory.Exists(path)) { + Logger.Info($"creating folder: {path}"); + Directory.CreateDirectory(path); + AccessUtils.GrantAccessToDirectory(path); + } + } + + public void WaitForCompletion() { + Task.WaitAll(ipcServer, eventServer); + } + + protected override void OnStop() { + Logger.Info("ziti-monitor OnStop was called"); + base.OnStop(); + } + + protected override void OnPause() { + Logger.Info("ziti-monitor OnPause was called"); + base.OnPause(); + } + + protected override void OnShutdown() { + Logger.Info("ziti-monitor OnShutdown was called"); + base.OnShutdown(); + } + + protected override void OnContinue() { + Logger.Info("ziti-monitor OnContinue was called"); + base.OnContinue(); + } + + protected override void OnCustomCommand(int command) { + Logger.Info("ziti-monitor OnCustomCommand was called {0}", command); + base.OnCustomCommand(command); + } + + protected override void OnSessionChange(SessionChangeDescription changeDescription) { + Logger.Info("ziti-monitor OnSessionChange was called {0}", changeDescription); + base.OnSessionChange(changeDescription); + } + + protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) { + Logger.Info("ziti-monitor OnPowerEvent was called {0}", powerStatus); + if(powerStatus == PowerBroadcastStatus.Suspend) { + // when going to sleep, make sure the healthcheck is disabled or accounts for going to sleep + disableHealthCheck(); + } + return base.OnPowerEvent(powerStatus); + } + + private void SetupServiceWatchers() { + var updateTimerInterval = ConfigurationManager.AppSettings.Get("UpdateTimer"); + var upInt = TimeSpan.Zero; + if (!TimeSpan.TryParse(updateTimerInterval, out upInt)) { + upInt = new TimeSpan(0, 10, 0); + } + + if (upInt.TotalMilliseconds < 10 * 60 * 1000) { + Logger.Warn("provided time [{0}] is too small. Using 10 minutes.", updateTimerInterval); #if MOCKUPDATE || ALLOWFASTINTERVAL - Logger.Info("MOCKUPDATE detected. Not limiting check to 10 minutes"); + Logger.Info("MOCKUPDATE detected. Not limiting check to 10 minutes"); #else upInt = TimeSpan.Parse("0:10:0"); #endif - } - - _updateTimer = new System.Timers.Timer(); - _updateTimer.Elapsed += CheckUpdate; - _updateTimer.Interval = upInt.TotalMilliseconds; - _updateTimer.Enabled = true; - _updateTimer.Start(); - Logger.Info("Version Checker is running every {0} minutes", upInt.TotalMinutes); - - cleanOldLogs(asmDir); - scanForStaleDownloads(updateFolder); - - checkUpdateImmediately(); - - try { - dataClient.ConnectAsync().Wait(); - } catch { - dataClient.Reconnect(); - } - - dataClient.WaitForConnectionAsync().Wait(); - } - - private void cleanOldLogs(string whereToScan) { - //this function will be removed in the future. it's here to clean out the old ziti-monitor*log files that - //were there before the 1.5.0 release - try { - Logger.Info("Scanning for stale logs"); - foreach (var f in Directory.EnumerateFiles(whereToScan)) { - FileInfo logFile = new FileInfo(f); - if (logFile.Name.StartsWith("ziti-monitor.") && logFile.Name.EndsWith(".log")) { - Logger.Info("removing old log file: " + logFile.Name); - logFile.Delete(); - } - } - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error has occurred"); - } - } + } + + _updateTimer = new System.Timers.Timer(); + _updateTimer.Elapsed += CheckUpdate; + _updateTimer.Interval = upInt.TotalMilliseconds; + _updateTimer.Enabled = true; + _updateTimer.Start(); + Logger.Info("Version Checker is running every {0} minutes", upInt.TotalMinutes); + + cleanOldLogs(asmDir); + scanForStaleDownloads(updateFolder); + + checkUpdateImmediately(); + + try { + dataClient.ConnectAsync().Wait(); + } catch { + dataClient.Reconnect(); + } + + dataClient.WaitForConnectionAsync().Wait(); + } + + private void cleanOldLogs(string whereToScan) { + //this function will be removed in the future. it's here to clean out the old ziti-monitor*log files that + //were there before the 1.5.0 release + try { + Logger.Info("Scanning for stale logs"); + foreach (var f in Directory.EnumerateFiles(whereToScan)) { + FileInfo logFile = new FileInfo(f); + if (logFile.Name.StartsWith("ziti-monitor.") && logFile.Name.EndsWith(".log")) { + Logger.Info("removing old log file: " + logFile.Name); + logFile.Delete(); + } + } + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error has occurred"); + } + } #if MOCKUPDATE static DateTime mockDate = DateTime.Now; #endif - private UpdateCheck getCheck(Version v) { + private UpdateCheck getCheck(Version v) { #if MOCKUPDATE //run with MOCKUPDATE to enable debugging/mocking the update check var check = new FilesystemCheck(v, -1, mockDate, "FilesysteCheck.download.mock.txt", new Version("2.1.4")); #else - if (string.IsNullOrEmpty(CurrentSettings.AutomaticUpdateURL)) { - CurrentSettings.AutomaticUpdateURL = GithubAPI.ProdUrl; - Logger.Info("Settings does not contain update url. Setting to: {}", CurrentSettings.AutomaticUpdateURL); - CurrentSettings.Write(); - } else { - Logger.Info("Settings contained a value for update url. Using: {}", CurrentSettings.AutomaticUpdateURL); - } + if (string.IsNullOrEmpty(CurrentSettings.AutomaticUpdateURL)) { + CurrentSettings.AutomaticUpdateURL = GithubAPI.ProdUrl; + Logger.Info("Settings does not contain update url. Setting to: {}", CurrentSettings.AutomaticUpdateURL); + CurrentSettings.Write(); + } else { + Logger.Info("Settings contained a value for update url. Using: {}", CurrentSettings.AutomaticUpdateURL); + } - var check = new GithubCheck(v, CurrentSettings.AutomaticUpdateURL); + var check = new GithubCheck(v, CurrentSettings.AutomaticUpdateURL); #endif - return check; - } - - private InstallationNotificationEvent newInstallationNotificationEvent(string version) { - InstallationNotificationEvent info = new InstallationNotificationEvent() { - Code = 0, - Error = "", - Message = "InstallationUpdate", - Type = "Notification", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString().ToLower(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - ZDEVersion = version, - }; - return info; - } - - private void CheckUpdate(object sender, ElapsedEventArgs e) { - if (e != null) { - Logger.Debug("Timer triggered CheckUpdate at {0}", e.SignalTime); - } - semaphore.Wait(); - - try { - Logger.Debug("checking for update"); - var check = getCheck(assemblyVersion); - - if (check.Avail >= 0) { - Logger.Debug("update check complete. no update available"); - semaphore.Release(); - return; - } - - Logger.Info("update is available."); - if (!Directory.Exists(updateFolder)) { - Directory.CreateDirectory(updateFolder); - } - InstallationNotificationEvent info = newInstallationNotificationEvent(check.GetNextVersion().ToString()); - info.PublishTime = check.PublishDate; - info.NotificationDuration = InstallationReminder(); - if (InstallationIsCritical(check.PublishDate)) { - info.InstallTime = DateTime.Now + TimeSpan.Parse("0:0:30"); - Logger.Warn("Installation is critical! for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); - NotifyInstallationUpdates(info, true); - if (CurrentSettings.AutomaticUpdatesDisabled) { - Logger.Debug("AutomaticUpdatesDisabled is set to true. Automatic update is disabled."); - } else { - Thread.Sleep(30); - installZDE(check); - } - } else { - info.InstallTime = InstallDateFromPublishDate(check.PublishDate); - Logger.Info("Installation reminder for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); - NotifyInstallationUpdates(info); - } - lastUpdateCheck = check; - lastInstallationNotification = info; - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error has occurred during the check for ZDE updates"); - } - semaphore.Release(); - } - - private void installZDE(UpdateCheck check) { - string fileDestination = Path.Combine(updateFolder, check?.FileName); - - if (check.AlreadyDownloaded(updateFolder, check.FileName)) { - Logger.Trace("package has already been downloaded to {0}", fileDestination); - } else { - Logger.Info("copying update package begins"); - check.CopyUpdatePackage(updateFolder, check.FileName); - Logger.Info("copying update package complete"); - } - - Logger.Info("package is in {0} - moving to install phase", fileDestination); - - if (!check.HashIsValid(updateFolder, check.FileName)) { - Logger.Warn("The file was downloaded but the hash is not valid. The file will be removed: {0}", fileDestination); - File.Delete(fileDestination); - return; - } - Logger.Debug("downloaded file hash was correct. update can continue."); + return check; + } + + private InstallationNotificationEvent newInstallationNotificationEvent(string version) { + InstallationNotificationEvent info = new InstallationNotificationEvent() { + Code = 0, + Error = "", + Message = "InstallationUpdate", + Type = "Notification", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString().ToLower(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + ZDEVersion = version, + }; + return info; + } + + private void CheckUpdate(object sender, ElapsedEventArgs e) { + if (e != null) { + Logger.Debug("Timer triggered CheckUpdate at {0}", e.SignalTime); + } + semaphore.Wait(); + + try { + Logger.Debug("checking for update"); + var check = getCheck(assemblyVersion); + + if (check.Avail >= 0) { + Logger.Debug("update check complete. no update available"); + semaphore.Release(); + return; + } + + Logger.Info("update is available."); + if (!Directory.Exists(updateFolder)) { + Directory.CreateDirectory(updateFolder); + } + InstallationNotificationEvent info = newInstallationNotificationEvent(check.GetNextVersion().ToString()); + info.PublishTime = check.PublishDate; + info.NotificationDuration = InstallationReminder(); + if (InstallationIsCritical(check.PublishDate)) { + info.InstallTime = DateTime.Now + TimeSpan.Parse("0:0:30"); + Logger.Warn("Installation is critical! for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); + NotifyInstallationUpdates(info, true); + if (CurrentSettings.AutomaticUpdatesDisabled) { + Logger.Debug("AutomaticUpdatesDisabled is set to true. Automatic update is disabled."); + } else { + Thread.Sleep(30); + installZDE(check); + } + } else { + info.InstallTime = InstallDateFromPublishDate(check.PublishDate); + Logger.Info("Installation reminder for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); + NotifyInstallationUpdates(info); + } + lastUpdateCheck = check; + lastInstallationNotification = info; + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error has occurred during the check for ZDE updates"); + } + semaphore.Release(); + } + + private void installZDE(UpdateCheck check) { + string fileDestination = Path.Combine(updateFolder, check?.FileName); + + if (check.AlreadyDownloaded(updateFolder, check.FileName)) { + Logger.Trace("package has already been downloaded to {0}", fileDestination); + } else { + Logger.Info("copying update package begins"); + check.CopyUpdatePackage(updateFolder, check.FileName); + Logger.Info("copying update package complete"); + } + + Logger.Info("package is in {0} - moving to install phase", fileDestination); + + if (!check.HashIsValid(updateFolder, check.FileName)) { + Logger.Warn("The file was downloaded but the hash is not valid. The file will be removed: {0}", fileDestination); + File.Delete(fileDestination); + return; + } + Logger.Debug("downloaded file hash was correct. update can continue."); #if !SKIPUPDATE try { - Logger.Info("verifying file [{}]", fileDestination); - new SignedFileValidator(fileDestination).Verify(); - Logger.Info("SignedFileValidator complete"); + Logger.Info("verifying file [{}]", fileDestination); + new SignedFileValidator(fileDestination).Verify(); + Logger.Info("SignedFileValidator complete"); - StopZiti(); + StopZiti(); StopUI().Wait(); Logger.Info("Running update package: " + fileDestination); @@ -841,257 +821,263 @@ private void installZDE(UpdateCheck check) { Logger.Error(ex, "Unexpected error during installation"); } #else - Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); - Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); - Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); + Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); + Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); + Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); #endif - } - - private bool isOlder(Version current) { - int compare = current.CompareTo(assemblyVersion); - Logger.Info("comparing current[{0}] to compare[{1}]: {2}", current.ToString(), assemblyVersion.ToString(), compare); - if (compare < 0) { - return true; - } else if (compare > 0) { - return false; - } else { - return false; - } - } - - private void scanForStaleDownloads(string folder) { - try { - if (!Directory.Exists(folder)) { - Logger.Debug("folder {0} does not exist. skipping", folder); - return; - } - Logger.Info("Scanning for stale downloads"); - foreach (var f in Directory.EnumerateFiles(folder)) { - try { - FileInfo fi = new FileInfo(f); - if (fi.Exists) { - if (fi.Name.StartsWith(filePrefix)) { - Logger.Debug("scanning for staleness: " + f); - string ver = Path.GetFileNameWithoutExtension(f).Substring(filePrefix.Length); - Version fileVersion = Version.Parse(ver); - if (isOlder(fileVersion)) { - Logger.Info("Removing old download: " + fi.Name); - fi.Delete(); - } else { - Logger.Debug("Retaining file. {1} is the same or newer than {1}", fi.Name, assemblyVersion); - } - } else { - Logger.Debug("skipping file named {0}", f); - } - } else { - Logger.Debug("file named {0} did not exist?", f); - } - } catch (Exception ex) { - Logger.Error(ex, "Unexpected exception processing {0}", f); - } - } - } catch (Exception ex) { - Logger.Error(ex, "Unexpected exception"); - } - } - - private void StopZiti() { - Logger.Info("Stopping the ziti service..."); - controller = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == "ziti"); - bool cleanStop = false; - if (controller != null && controller.Status != ServiceControllerStatus.Stopped) { - try { - controller.Stop(); - Logger.Debug("Waiting for the ziti service to stop."); - controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); - Logger.Debug("The ziti service was stopped successfully."); - cleanStop = true; - } catch (Exception e) { - Logger.Error(e, "Timout while trying to stop service!"); - } - } else { - Logger.Debug("The ziti has ALREADY been stopped successfully."); - } - if (!cleanStop) { - Logger.Debug("Stopping ziti-edge-tunnel forcefully."); - stopProcessForcefully("ziti-edge-tunnel", "data service [ziti]"); - } - } - - private void stopProcessForcefully(string processName, string description) { - try { - Logger.Info("Closing the {description} process", description); - Process[] workers = Process.GetProcessesByName(processName); - if (workers.Length < 1) { - Logger.Info("No {description} process found to close.", description); - return; - } - foreach (Process worker in workers) { - try { - Logger.Info("Killing: {0}", worker); - if (!worker.CloseMainWindow()) { - //don't care right now because when called on the UI it just gets 'hidden' - } - worker.Kill(); - worker.WaitForExit(5000); - Logger.Info("Stopping the {description} process exited cleanly", description); - worker.Dispose(); - } catch (Exception e) { - Logger.Error(e, "Unexpected error when closing the {description}!", description); - } - } - } catch (Exception e) { - Logger.Error(e, "Unexpected error when closing the {description}!", description); - } - } - - async private Task StopUI() { - //first try to ask the UI to exit: - - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 0, - Error = "", - Message = "Upgrading" - }; - EventRegistry.SendEventToConsumers(status); - - await Task.Delay(1000); //wait for the event to send and give the UI time to close... - - stopProcessForcefully("ZitiDesktopEdge", "UI"); - } - - private static void Svc_OnShutdownEvent(object sender, StatusEvent e) { - Logger.Info("the service is shutting down normally..."); - - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 0, - Error = "SERVICE DOWN", - Message = "SERVICE DOWN", - Status = ServiceActions.ServiceStatus() - }; - EventRegistry.SendEventToConsumers(status); - } - - private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { - SetLogLevel(e.LogLevel); - } - - private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { - Logger.Trace("Notification event but not acting: {0}", e.Op); - } - - private void Svc_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { - string dns = e?.Status?.IpInfo?.DNS; - string version = e?.Status?.ServiceVersion.Version; - string op = e?.Op; - Logger.Info($"Operation {op}. running dns: {dns} at version {version}"); - - try { - dnsIpAddress = IPAddress.Parse(dns); - } catch { - //ignore it - } - SetLogLevel(e.Status.LogLevel); - } - - private void Svc_OnClientConnected(object sender, object e) { - Logger.Info("successfully connected to service"); - if (!dnsProbeTimer.Enabled) { - dnsProbeTimer.Enabled = true; - dnsProbeTimer.Start(); - Logger.Info("DNS Probe enabled"); - } - } - - private void Svc_OnClientDisconnected(object sender, object e) { - //dnsProbeTimer.Stop(); - DataClient svc = (DataClient)sender; - if (svc.CleanShutdown) { - //then this is fine and expected - the service is shutting down - Logger.Info("client disconnected due to clean service shutdown"); - - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 0, - Error = "SERVICE DOWN", - Message = "SERVICE DOWN", - Status = ServiceActions.ServiceStatus(), - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - }; - EventRegistry.SendEventToConsumers(status); - } else { - Logger.Error("SERVICE IS DOWN and did not exit cleanly."); - - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 10, - Error = "SERVICE DOWN", - Message = "SERVICE DOWN", - Type = "Status", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - }; - EventRegistry.SendEventToConsumers(status); - } - } - - private static void EnumerateDNS() { - var ps = System.Management.Automation.PowerShell.Create(); - ps.AddScript("Get-DnsClientServerAddress"); - var results = ps.Invoke(); - - using (StringWriter sw = new StringWriter()) { - foreach (var r in results) { - string name = (string)r.Properties["InterfaceAlias"].Value; - string[] dnses = (string[])r.Properties["ServerAddresses"].Value; - sw.WriteLine($"Interface: {name}. DNS: {string.Join(",", dnses)}"); - } - Logger.Info("DNS RESULTS:\n{0}", sw.ToString()); - } - } - - private TimeSpan InstallationReminder() { - var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationReminder"); - var reminderInt = TimeSpan.Zero; - if (!TimeSpan.TryParse(installationReminderIntervalStr, out reminderInt)) { - reminderInt = new TimeSpan(0, 1, 0); - } - return reminderInt; - } - - private DateTime InstallDateFromPublishDate(DateTime publishDate) { - var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); - var instCritTimespan = TimeSpan.Zero; - if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { - instCritTimespan = TimeSpan.Parse("7:0:0:0"); - } - return publishDate + instCritTimespan; - } - - private bool InstallationIsCritical(DateTime publishDate) { - var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); - var instCritTimespan = TimeSpan.Zero; - if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { - instCritTimespan = TimeSpan.Parse("7:0:0:0"); - } - return DateTime.Now > publishDate + instCritTimespan; - } - - private void NotifyInstallationUpdates(InstallationNotificationEvent evt) { - NotifyInstallationUpdates(evt, false); - } - - private void NotifyInstallationUpdates(InstallationNotificationEvent evt, bool force) { - try { - evt.Message = "InstallationUpdate"; - evt.Type = "Notification"; - EventRegistry.SendEventToConsumers(evt); - Logger.Debug("NotifyInstallationUpdates: sent for version {0} is sent to the events pipe...", evt.ZDEVersion); - return; - } catch (Exception e) { - Logger.Error("The notification for the installation updates for version {0} has failed: {1}", evt.ZDEVersion, e); - } - } - } + } + + private bool isOlder(Version current) { + int compare = current.CompareTo(assemblyVersion); + Logger.Info("comparing current[{0}] to compare[{1}]: {2}", current.ToString(), assemblyVersion.ToString(), compare); + if (compare < 0) { + return true; + } else if (compare > 0) { + return false; + } else { + return false; + } + } + + private void scanForStaleDownloads(string folder) { + try { + if (!Directory.Exists(folder)) { + Logger.Debug("folder {0} does not exist. skipping", folder); + return; + } + Logger.Info("Scanning for stale downloads"); + foreach (var f in Directory.EnumerateFiles(folder)) { + try { + FileInfo fi = new FileInfo(f); + if (fi.Exists) { + if (fi.Name.StartsWith(filePrefix)) { + Logger.Debug("scanning for staleness: " + f); + string ver = Path.GetFileNameWithoutExtension(f).Substring(filePrefix.Length); + Version fileVersion = Version.Parse(ver); + if (isOlder(fileVersion)) { + Logger.Info("Removing old download: " + fi.Name); + fi.Delete(); + } else { + Logger.Debug("Retaining file. {1} is the same or newer than {1}", fi.Name, assemblyVersion); + } + } else { + Logger.Debug("skipping file named {0}", f); + } + } else { + Logger.Debug("file named {0} did not exist?", f); + } + } catch (Exception ex) { + Logger.Error(ex, "Unexpected exception processing {0}", f); + } + } + } catch (Exception ex) { + Logger.Error(ex, "Unexpected exception"); + } + } + + private void StopZiti() { + Logger.Info("Stopping the ziti service..."); + controller = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == "ziti"); + bool cleanStop = false; + if (controller != null && controller.Status != ServiceControllerStatus.Stopped) { + try { + controller.Stop(); + Logger.Debug("Waiting for the ziti service to stop."); + controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); + Logger.Debug("The ziti service was stopped successfully."); + cleanStop = true; + } catch (Exception e) { + Logger.Error(e, "Timout while trying to stop service!"); + } + } else { + Logger.Debug("The ziti has ALREADY been stopped successfully."); + } + if (!cleanStop) { + Logger.Debug("Stopping ziti-edge-tunnel forcefully."); + stopProcessForcefully("ziti-edge-tunnel", "data service [ziti]"); + } + } + + private void stopProcessForcefully(string processName, string description) { + try { + Logger.Info("Closing the {description} process", description); + Process[] workers = Process.GetProcessesByName(processName); + if (workers.Length < 1) { + Logger.Info("No {description} process found to close.", description); + return; + } + // though strange, because we're about to kill the process, this is still + // considered 'expected' since the monitor service is shutting it down (forcefully). + // not clean is to indicate the process ended unexpectedly + dataClient.ExpectedShutdown = true; + + foreach (Process worker in workers) { + try { + Logger.Info("Killing: {0}", worker); + if (!worker.CloseMainWindow()) { + //don't care right now because when called on the UI it just gets 'hidden' + } + worker.Kill(); + worker.WaitForExit(5000); + Logger.Info("Stopping the {description} process killed", description); + worker.Dispose(); + } catch (Exception e) { + Logger.Error(e, "Unexpected error when closing the {description}!", description); + } + } + } catch (Exception e) { + Logger.Error(e, "Unexpected error when closing the {description}!", description); + } + } + + async private Task StopUI() { + //first try to ask the UI to exit: + + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 0, + Error = "", + Message = "Upgrading" + }; + EventRegistry.SendEventToConsumers(status); + + await Task.Delay(1000); //wait for the event to send and give the UI time to close... + + stopProcessForcefully("ZitiDesktopEdge", "UI"); + } + + private static void Svc_OnShutdownEvent(object sender, StatusEvent e) { + Logger.Info("the service is shutting down normally..."); + + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 0, + Error = "SERVICE DOWN", + Message = "SERVICE DOWN", + Status = ServiceActions.ServiceStatus() + }; + EventRegistry.SendEventToConsumers(status); + } + + private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { + SetLogLevel(e.LogLevel); + } + + private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { + Logger.Trace("Notification event but not acting: {0}", e.Op); + } + + private void Svc_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { + string dns = e?.Status?.IpInfo?.DNS; + string version = e?.Status?.ServiceVersion.Version; + string op = e?.Op; + Logger.Info($"Operation {op}. running dns: {dns} at version {version}"); + + SetLogLevel(e.Status.LogLevel); + } + + private void disableHealthCheck() { + if (zetHealthcheck.Enabled) { + zetHealthcheck.Enabled = true; + zetHealthcheck.Stop(); + Logger.Info("ziti-edge-tunnel health check disabled"); + } else { + Logger.Info("ziti-edge-tunnel health check already disabled"); + } + } + + private void enableHealthCheck() { + if (!zetHealthcheck.Enabled) { + zetHealthcheck.Enabled = false; + zetHealthcheck.Start(); + Logger.Info("ziti-edge-tunnel health check enabled"); + } else { + Logger.Info("ziti-edge-tunnel health check already enabled"); + } + } + + private void Svc_OnClientConnected(object sender, object e) { + Logger.Info("successfully connected to service"); + enableHealthCheck(); + } + + private void Svc_OnClientDisconnected(object sender, object e) { + disableHealthCheck(); //no need to healthcheck when we know it's disconnected + DataClient svc = (DataClient)sender; + if (svc.ExpectedShutdown) { + //then this is fine and expected - the service is shutting down + Logger.Info("client disconnected due to clean service shutdown"); + } else { + Logger.Error("SERVICE IS DOWN and did not exit cleanly."); + + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 10, + Error = "SERVICE DOWN", + Message = "SERVICE DOWN", + Type = "Status", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + }; + EventRegistry.SendEventToConsumers(status); + } + } + + private static void EnumerateDNS() { + var ps = System.Management.Automation.PowerShell.Create(); + ps.AddScript("Get-DnsClientServerAddress"); + var results = ps.Invoke(); + + using (StringWriter sw = new StringWriter()) { + foreach (var r in results) { + string name = (string)r.Properties["InterfaceAlias"].Value; + string[] dnses = (string[])r.Properties["ServerAddresses"].Value; + sw.WriteLine($"Interface: {name}. DNS: {string.Join(",", dnses)}"); + } + Logger.Info("DNS RESULTS:\n{0}", sw.ToString()); + } + } + + private TimeSpan InstallationReminder() { + var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationReminder"); + var reminderInt = TimeSpan.Zero; + if (!TimeSpan.TryParse(installationReminderIntervalStr, out reminderInt)) { + reminderInt = new TimeSpan(0, 1, 0); + } + return reminderInt; + } + + private DateTime InstallDateFromPublishDate(DateTime publishDate) { + var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); + var instCritTimespan = TimeSpan.Zero; + if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { + instCritTimespan = TimeSpan.Parse("7:0:0:0"); + } + return publishDate + instCritTimespan; + } + + private bool InstallationIsCritical(DateTime publishDate) { + var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); + var instCritTimespan = TimeSpan.Zero; + if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { + instCritTimespan = TimeSpan.Parse("7:0:0:0"); + } + return DateTime.Now > publishDate + instCritTimespan; + } + + private void NotifyInstallationUpdates(InstallationNotificationEvent evt) { + NotifyInstallationUpdates(evt, false); + } + + private void NotifyInstallationUpdates(InstallationNotificationEvent evt, bool force) { + try { + evt.Message = "InstallationUpdate"; + evt.Type = "Notification"; + EventRegistry.SendEventToConsumers(evt); + Logger.Debug("NotifyInstallationUpdates: sent for version {0} is sent to the events pipe...", evt.ZDEVersion); + return; + } catch (Exception e) { + Logger.Error("The notification for the installation updates for version {0} has failed: {1}", evt.ZDEVersion, e); + } + } + } } \ No newline at end of file diff --git a/ZitiUpdateService/Utils.cs b/ZitiUpdateService/Utils.cs index dd6e5722c..540fdfaf1 100644 --- a/ZitiUpdateService/Utils.cs +++ b/ZitiUpdateService/Utils.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Security.AccessControl; using System.Security.Principal; using System.IO; @@ -37,8 +37,7 @@ public static class AccessUtils { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); public static void GrantAccessToDirectory(string path) { - try - { + try { if (!Directory.Exists(path)) { return; } @@ -48,7 +47,7 @@ public static void GrantAccessToDirectory(string path) { SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null); sec.AddAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.Modify | FileSystemRights.Synchronize, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow)); Directory.SetAccessControl(path, sec); - } catch(Exception e) { + } catch (Exception e) { Logger.Error(e, "Unexpected error when setting directory security: {0}", e.Message); } } diff --git a/ZitiUpdateService/ZitiUpdateService-log.config b/ZitiUpdateService/ZitiUpdateService-log.config index 6184e0bee..27be79ed0 100644 --- a/ZitiUpdateService/ZitiUpdateService-log.config +++ b/ZitiUpdateService/ZitiUpdateService-log.config @@ -5,7 +5,9 @@ - ()) { - string assetName = asset.Property("name").Value.ToString(); - - if (assetName.StartsWith("Ziti.Desktop.Edge.Client-")) { - downloadUrl = asset.Property("browser_download_url").Value.ToString(); - break; - } else { - Logger.Debug("skipping asset with name: {assetName}", assetName); - } - } - - if (downloadUrl == null) { - Logger.Error("DOWNLOAD URL not found at: {0}", updateCheckUrl); - return 0; - } - Logger.Debug("download url detected: {0}", downloadUrl); - FileName = downloadUrl.Substring(downloadUrl.LastIndexOf('/') + 1); - Logger.Debug("download file name: {0}", FileName); - - string releaseVersion = json.Property("tag_name").Value.ToString(); - string releaseName = json.Property("name").Value.ToString(); - - if(!Version.TryParse(releaseVersion, out nextVersion)) { - string msg = $"Could not parse version: {releaseVersion}"; - Logger.Error(msg); - throw new Exception(msg); - } - - string isoPublishedDate = json.Property("published_at").Value.ToString(); - PublishDate = DateTime.Parse(isoPublishedDate, null, System.Globalization.DateTimeStyles.RoundtripKind); - - int compare = currentVersion.CompareTo(nextVersion); - if (compare < 0) { - Logger.Info("upgrade {} is available. Published version: {} is newer than the current version: {}", releaseName, nextVersion, currentVersion); - } else if (compare > 0) { - Logger.Info("the version installed: {0} is newer than the released version: {1}", currentVersion, nextVersion); - } - return compare; - } - - override public bool HashIsValid(string destinationFolder, string destinationName) { - WebClient webClient = new WebClient(); - string sha256dest = Path.Combine(destinationFolder, destinationName + ".sha256"); - string downloadUrlsha256 = downloadUrl + ".sha256"; - Logger.Info("download started for: {0} to {1}", downloadUrlsha256, sha256dest); - webClient.DownloadFile(downloadUrlsha256, sha256dest); - Logger.Info("download complete to: {0}", sha256dest); - - string dest = Path.Combine(destinationFolder, destinationName); - string hash = File.ReadAllText(sha256dest); - - using (SHA256 hasher = SHA256.Create()) - using (FileStream stream = File.OpenRead(dest)) { - byte[] sha256bytes = hasher.ComputeHash(stream); - string computed = BitConverter.ToString(sha256bytes).Replace("-", ""); - - File.Delete(sha256dest); - Logger.Info("comparing computed hash: {0} to downloaded hash: {1}", computed, hash); - return computed.ToLower().Trim() == hash.ToLower().Trim(); - } + internal class GithubCheck : UpdateCheck { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + string updateCheckUrl; + string downloadUrl = null; + Version nextVersion = null; + + + public GithubCheck(Version compareTo, string url) : base(compareTo) { + this.updateCheckUrl = url; + Avail = CheckUpdate(compareTo); + } + + override public bool AlreadyDownloaded(string destinationFolder, string destinationName) { + return File.Exists(Path.Combine(destinationFolder, destinationName)); + } + + override public void CopyUpdatePackage(string destinationFolder, string destinationName) { + WebClient webClient = new WebClient(); + string dest = Path.Combine(destinationFolder, destinationName); + Logger.Info("download started for: {0} to {1}", downloadUrl, dest); + webClient.DownloadFile(downloadUrl, dest); + Logger.Info("download complete to: {0}", dest); } - override public Version GetNextVersion() { - return nextVersion; - } - - private void getReleaseInfoAfterCurrent(string releaseUrl, Version currentVersion, out DateTime _publishedDateAfterCurrent, out Version _versionAfterCurrent ) { - Logger.Debug("Fetching the releases info from {0}", releaseUrl); - JArray jArray = GithubAPI.GetJsonArray(releaseUrl); - string isoPublishedDate = null; - Version publishedReleaseVersion = null; - - if (jArray.HasValues) { - foreach(JObject json in jArray.Children()) { - string releaseVersion = json.Property("name").Value.ToString(); - Version normalizedReleaseVersion = null; - try { - normalizedReleaseVersion = Version.Parse(releaseVersion); - } catch (Exception e) { - try { - releaseVersion = json.Property("tag_name").Value.ToString(); - normalizedReleaseVersion = Version.Parse(releaseVersion); - } catch (Exception err) { - Logger.Error("Cound not fetch version from name due to {0} and tag_name due to {1}", e.Message, err.Message); - continue; - } - } - - if (normalizedReleaseVersion.CompareTo(currentVersion) <= 0) { - break; - } - isoPublishedDate = json.Property("published_at").Value.ToString(); - publishedReleaseVersion = normalizedReleaseVersion; - } - } - if (isoPublishedDate != null) { - _publishedDateAfterCurrent = DateTime.Parse(isoPublishedDate, null, System.Globalization.DateTimeStyles.RoundtripKind); - } else { - _publishedDateAfterCurrent = DateTime.Now; - } - _versionAfterCurrent = publishedReleaseVersion; - - } - - private DateTime getCreationTime(string publishedDateStr, string fileDestination) { - DateTime publishedDate = DateTime.Now; - - try { - DateTime.TryParse(publishedDateStr, out publishedDate); - } catch (Exception e) { - Logger.Error("Could not convert published date of the installer - input string : {0} due to {1}. Fetching download time instead.", publishedDateStr, e.Message); - try { - if (fileDestination != null) { - publishedDate = File.GetCreationTime(fileDestination); - } - } catch (Exception err) { - Logger.Error("Could not fetch creation date of the installer due to {0}.", err.Message); - } - - } - return publishedDate; - } - } + private int CheckUpdate(Version currentVersion) { + Logger.Debug("checking for update begins. current version detected as {0}", currentVersion); + Logger.Debug("issuing http get to url: {0}", updateCheckUrl); + + JObject json = GithubAPI.GetJson(updateCheckUrl); + JArray assets = JArray.Parse(json.Property("assets").Value.ToString()); + foreach (JObject asset in assets.Children()) { + string assetName = asset.Property("name").Value.ToString(); + + if (assetName.StartsWith("Ziti.Desktop.Edge.Client-")) { + downloadUrl = asset.Property("browser_download_url").Value.ToString(); + break; + } else { + Logger.Debug("skipping asset with name: {assetName}", assetName); + } + } + + if (downloadUrl == null) { + Logger.Error("DOWNLOAD URL not found at: {0}", updateCheckUrl); + return 0; + } + Logger.Debug("download url detected: {0}", downloadUrl); + FileName = downloadUrl.Substring(downloadUrl.LastIndexOf('/') + 1); + Logger.Debug("download file name: {0}", FileName); + + string releaseVersion = json.Property("tag_name").Value.ToString(); + string releaseName = json.Property("name").Value.ToString(); + + if (!Version.TryParse(releaseVersion, out nextVersion)) { + string msg = $"Could not parse version: {releaseVersion}"; + Logger.Error(msg); + throw new Exception(msg); + } + + string isoPublishedDate = json.Property("published_at").Value.ToString(); + PublishDate = DateTime.Parse(isoPublishedDate, null, System.Globalization.DateTimeStyles.RoundtripKind); + + int compare = currentVersion.CompareTo(nextVersion); + if (compare < 0) { + Logger.Info("upgrade {} is available. Published version: {} is newer than the current version: {}", releaseName, nextVersion, currentVersion); + } else if (compare > 0) { + Logger.Info("the version installed: {0} is newer than the released version: {1}", currentVersion, nextVersion); + } + return compare; + } + + override public bool HashIsValid(string destinationFolder, string destinationName) { + WebClient webClient = new WebClient(); + string sha256dest = Path.Combine(destinationFolder, destinationName + ".sha256"); + string downloadUrlsha256 = downloadUrl + ".sha256"; + Logger.Info("download started for: {0} to {1}", downloadUrlsha256, sha256dest); + webClient.DownloadFile(downloadUrlsha256, sha256dest); + Logger.Info("download complete to: {0}", sha256dest); + + string dest = Path.Combine(destinationFolder, destinationName); + string hash = File.ReadAllText(sha256dest); + + using (SHA256 hasher = SHA256.Create()) + using (FileStream stream = File.OpenRead(dest)) { + byte[] sha256bytes = hasher.ComputeHash(stream); + string computed = BitConverter.ToString(sha256bytes).Replace("-", ""); + + File.Delete(sha256dest); + Logger.Info("comparing computed hash: {0} to downloaded hash: {1}", computed, hash); + return computed.ToLower().Trim() == hash.ToLower().Trim(); + } + } + + override public Version GetNextVersion() { + return nextVersion; + } + + private void getReleaseInfoAfterCurrent(string releaseUrl, Version currentVersion, out DateTime _publishedDateAfterCurrent, out Version _versionAfterCurrent) { + Logger.Debug("Fetching the releases info from {0}", releaseUrl); + JArray jArray = GithubAPI.GetJsonArray(releaseUrl); + string isoPublishedDate = null; + Version publishedReleaseVersion = null; + + if (jArray.HasValues) { + foreach (JObject json in jArray.Children()) { + string releaseVersion = json.Property("name").Value.ToString(); + Version normalizedReleaseVersion = null; + try { + normalizedReleaseVersion = Version.Parse(releaseVersion); + } catch (Exception e) { + try { + releaseVersion = json.Property("tag_name").Value.ToString(); + normalizedReleaseVersion = Version.Parse(releaseVersion); + } catch (Exception err) { + Logger.Error("Cound not fetch version from name due to {0} and tag_name due to {1}", e.Message, err.Message); + continue; + } + } + + if (normalizedReleaseVersion.CompareTo(currentVersion) <= 0) { + break; + } + isoPublishedDate = json.Property("published_at").Value.ToString(); + publishedReleaseVersion = normalizedReleaseVersion; + } + } + if (isoPublishedDate != null) { + _publishedDateAfterCurrent = DateTime.Parse(isoPublishedDate, null, System.Globalization.DateTimeStyles.RoundtripKind); + } else { + _publishedDateAfterCurrent = DateTime.Now; + } + _versionAfterCurrent = publishedReleaseVersion; + + } + + private DateTime getCreationTime(string publishedDateStr, string fileDestination) { + DateTime publishedDate = DateTime.Now; + + try { + DateTime.TryParse(publishedDateStr, out publishedDate); + } catch (Exception e) { + Logger.Error("Could not convert published date of the installer - input string : {0} due to {1}. Fetching download time instead.", publishedDateStr, e.Message); + try { + if (fileDestination != null) { + publishedDate = File.GetCreationTime(fileDestination); + } + } catch (Exception err) { + Logger.Error("Could not fetch creation date of the installer due to {0}.", err.Message); + } + + } + return publishedDate; + } + } } diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs b/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs index 2567b8d00..0ea496e13 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Runtime.InteropServices; namespace ZitiUpdateService.Checkers.PeFile { @@ -275,7 +275,8 @@ public struct IMAGE_OPTIONAL_HEADER64 { [StructLayout(LayoutKind.Explicit)] public struct IMAGE_SECTION_HEADER { //FROM: http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html - [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + [FieldOffset(0)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public char[] Name; [FieldOffset(8)] public UInt32 VirtualSize; diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index f099387c1..18b7c88db 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections.Generic; using System.IO; using System.Net.Http; @@ -34,7 +34,7 @@ public class SignedFileValidator { public const int LengthOfChecksum = 4; //the checksum is 4 bytes public const int LengthCertificateTable = 8; //the Certificate Table is 8 bytes - + public HashPositions ImportantHashPositions { get; private set; } public string FilePath { get; private set; } public PeType Type { get; private set; } @@ -92,7 +92,7 @@ private void parse() { int magicNumber = BitConverter.ToInt16(magicNumberBytes, 0); Type = magicNumber == 0x10b ? PeType.Pe32 : PeType.Pe32Plus; stream.Seek(-2, SeekOrigin.Current); //skip back to before the magic number so the struct below is created properly - + //parse Optional Header Standard and Windows-Specific fields (66 or 88 bytes) //see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only if (Type == PeType.Pe32) { @@ -161,8 +161,7 @@ public List ExtractVerifiedSignatureCertificates() { SignerInfo innerSignerInfo = innerCms.SignerInfos[0]; try { innerSignerInfo.CheckSignature(false); - } - catch (CryptographicException) { + } catch (CryptographicException) { if (innerSignerInfo.Certificate.Thumbprint == oldKnownThumbprint) { //special handling for the known 'old' NetFoundry signing certificate, now expired... //TODO: remove this code after 2021 @@ -292,14 +291,12 @@ public async static Task VerifyTrust(X509Store cas, X509Certificate2 trust } else { exceptions.Add(new CryptographicException("Could not retrieve revocation list from " + url + "- cannot verify trust")); } - } - catch (Exception innerException) { + } catch (Exception innerException) { exceptions.Add(new CryptographicException("crl at " + url + " could not be used", innerException)); } } } - } - catch (Exception ex) { + } catch (Exception ex) { exceptions.Add(ex); } } @@ -483,7 +480,7 @@ 15. Finalize the hash algorithm context. internal bool reorderNeeded = false; private uint lastAddress = 0; public void AddSectionTableHeader(IMAGE_SECTION_HEADER sectionTableHeader) { - + if (sectionTableHeader.SizeOfRawData > 0) { SectionTableHeaders.Add(sectionTableHeader); } @@ -560,7 +557,7 @@ internal CrlDistributionPointParser(MemoryStream asn1Data) : this(null, asn1Data internal CrlDistributionPointParser(CrlDistributionPointParser parent, MemoryStream memoryStream, /*BinaryReader asn1Reader,*/ int offset) { BinaryReader asn1Reader = new BinaryReader(memoryStream); this.offset = offset; - type = readTag(asn1Reader); + type = readTag(asn1Reader); length = readTagLen(asn1Reader); this.parent = parent; int headerBytes = consumedBytes; @@ -568,7 +565,7 @@ internal CrlDistributionPointParser(CrlDistributionPointParser parent, MemoryStr switch (type) { case AsnTagType.SEQUENCE: case AsnTagType.ARRAY_ELEMENT: - var t = new CrlDistributionPointParser(this, memoryStream, (int) memoryStream.Position); + var t = new CrlDistributionPointParser(this, memoryStream, (int)memoryStream.Position); consumedBytes += t.consumedBytes; tags.Add(t); this.urls.AddRange(t.urls); diff --git a/ZitiUpdateService/checkers/PeFile/StructHelper.cs b/ZitiUpdateService/checkers/PeFile/StructHelper.cs index d53b3c504..f8ce330f0 100644 --- a/ZitiUpdateService/checkers/PeFile/StructHelper.cs +++ b/ZitiUpdateService/checkers/PeFile/StructHelper.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System.Runtime.InteropServices; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Runtime.InteropServices; namespace ZitiUpdateService.Checkers.PeFile { public static class StructHelper { diff --git a/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs index 6298fedb6..580f2fc61 100644 --- a/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs +++ b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; diff --git a/ZitiUpdateService/checkers/UpdateCheckers.cs b/ZitiUpdateService/checkers/UpdateCheckers.cs index 56bebbdec..dfa4105c0 100644 --- a/ZitiUpdateService/checkers/UpdateCheckers.cs +++ b/ZitiUpdateService/checkers/UpdateCheckers.cs @@ -1,41 +1,39 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using NLog; namespace ZitiUpdateService.Checkers { - abstract class UpdateCheck - { - public DateTime PublishDate { get; set; } - public string FileName { get; set; } - public int Avail { get; set; } + abstract class UpdateCheck { + public DateTime PublishDate { get; set; } + public string FileName { get; set; } + public int Avail { get; set; } - internal Version compareTo; + internal Version compareTo; - public UpdateCheck(Version current) - { - compareTo = current; + public UpdateCheck(Version current) { + compareTo = current; } public abstract void CopyUpdatePackage(string destinationFolder, string destinationName); public abstract bool AlreadyDownloaded(string destinationFolder, string destinationName); public abstract bool HashIsValid(string destinationFolder, string destinationName); - public abstract Version GetNextVersion(); + public abstract Version GetNextVersion(); - } + } } diff --git a/ZitiUpdateService/utils/Settings.cs b/ZitiUpdateService/utils/Settings.cs index 4307423c7..38ce43766 100644 --- a/ZitiUpdateService/utils/Settings.cs +++ b/ZitiUpdateService/utils/Settings.cs @@ -1,129 +1,129 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; using System.IO; using Newtonsoft.Json; using NLog; using ZitiDesktopEdge.DataStructures; namespace ZitiUpdateService.Utils { - internal class Settings { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private FileSystemWatcher watcher; - - [JsonIgnore] - private string Location { get; set; } - - public bool AutomaticUpdatesDisabled { get; set; } - public string AutomaticUpdateURL { get; set; } - - public event System.EventHandler OnConfigurationChange; - - internal Settings(bool doInit) { - if (doInit) { - init(); - } - } - - public Settings() { - } - - private void init() { - string folder = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "NetFoundry", "ZitiUpdateService"); - string file = "settings.json"; - Location = Path.Combine(folder, file); - Directory.CreateDirectory(folder); - watcher = new FileSystemWatcher(folder); - watcher.Filter = file; - - watcher.NotifyFilter = NotifyFilters.Attributes - | NotifyFilters.CreationTime - | NotifyFilters.DirectoryName - | NotifyFilters.FileName - | NotifyFilters.LastAccess - | NotifyFilters.LastWrite - | NotifyFilters.Security - | NotifyFilters.Size; - watcher.Changed += OnChanged; - watcher.Deleted += OnDeleted; - watcher.Renamed += OnRenamed; - watcher.Error += OnError; - watcher.EnableRaisingEvents = true; - } - - private static JsonSerializer serializer = new JsonSerializer() { Formatting = Formatting.Indented }; - internal void Load() { - try { - string json = File.ReadAllText(Location); - var jsonReaderEvt = new JsonTextReader(new StringReader(json)); - Settings s = serializer.Deserialize(jsonReaderEvt); - if (s != null) { - Update(s); - } else { - Logger.Debug("settings file was null? file doesn't exist or file was garbage?"); - } - } catch (Exception ex) { - // do nothing, probably means the file is just or doesn't exist etc. - Logger.Debug("unexpected error loading settings. file was null? file doesn't exist or file was garbage? {0}", ex); - } - } - internal void Write() { - lock (this) { - this.watcher.Changed -= OnChanged; - try { - using (StreamWriter file = File.CreateText(Location)) { - serializer.Serialize(file, this); - file.Flush(); - file.Close(); - } - this.OnConfigurationChange?.Invoke(null, null); - } catch { - // do nothing - } - this.watcher.Changed += OnChanged; - } - } - - - private static void OnError(object sender, ErrorEventArgs e) { - } - - private void OnRenamed(object sender, RenamedEventArgs e) { - Logger.Info("Settings file renamed. Resetting to defaults..."); - this.Update(new Settings()); - } - - private void OnDeleted(object sender, FileSystemEventArgs e) { - Logger.Info("Settings file deleted. Resetting to defaults..."); - this.Update(new Settings()); - } - - private void OnCreated(object sender, FileSystemEventArgs e) { - } - - private void OnChanged(object sender, FileSystemEventArgs e) { - Logger.Info("Settings file changed. Reloading..."); - this.Load(); - this.OnConfigurationChange?.Invoke(null, null); - } - - private void Update(Settings source) { - this.AutomaticUpdatesDisabled = source.AutomaticUpdatesDisabled; - this.AutomaticUpdateURL = source.AutomaticUpdateURL; - } - } + internal class Settings { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private FileSystemWatcher watcher; + + [JsonIgnore] + private string Location { get; set; } + + public bool AutomaticUpdatesDisabled { get; set; } + public string AutomaticUpdateURL { get; set; } + + public event System.EventHandler OnConfigurationChange; + + internal Settings(bool doInit) { + if (doInit) { + init(); + } + } + + public Settings() { + } + + private void init() { + string folder = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "NetFoundry", "ZitiUpdateService"); + string file = "settings.json"; + Location = Path.Combine(folder, file); + Directory.CreateDirectory(folder); + watcher = new FileSystemWatcher(folder); + watcher.Filter = file; + + watcher.NotifyFilter = NotifyFilters.Attributes + | NotifyFilters.CreationTime + | NotifyFilters.DirectoryName + | NotifyFilters.FileName + | NotifyFilters.LastAccess + | NotifyFilters.LastWrite + | NotifyFilters.Security + | NotifyFilters.Size; + watcher.Changed += OnChanged; + watcher.Deleted += OnDeleted; + watcher.Renamed += OnRenamed; + watcher.Error += OnError; + watcher.EnableRaisingEvents = true; + } + + private static JsonSerializer serializer = new JsonSerializer() { Formatting = Formatting.Indented }; + internal void Load() { + try { + string json = File.ReadAllText(Location); + var jsonReaderEvt = new JsonTextReader(new StringReader(json)); + Settings s = serializer.Deserialize(jsonReaderEvt); + if (s != null) { + Update(s); + } else { + Logger.Debug("settings file was null? file doesn't exist or file was garbage?"); + } + } catch (Exception ex) { + // do nothing, probably means the file is just or doesn't exist etc. + Logger.Debug("unexpected error loading settings. file was null? file doesn't exist or file was garbage? {0}", ex); + } + } + internal void Write() { + lock (this) { + this.watcher.Changed -= OnChanged; + try { + using (StreamWriter file = File.CreateText(Location)) { + serializer.Serialize(file, this); + file.Flush(); + file.Close(); + } + this.OnConfigurationChange?.Invoke(null, null); + } catch { + // do nothing + } + this.watcher.Changed += OnChanged; + } + } + + + private static void OnError(object sender, ErrorEventArgs e) { + } + + private void OnRenamed(object sender, RenamedEventArgs e) { + Logger.Info("Settings file renamed. Resetting to defaults..."); + this.Update(new Settings()); + } + + private void OnDeleted(object sender, FileSystemEventArgs e) { + Logger.Info("Settings file deleted. Resetting to defaults..."); + this.Update(new Settings()); + } + + private void OnCreated(object sender, FileSystemEventArgs e) { + } + + private void OnChanged(object sender, FileSystemEventArgs e) { + Logger.Info("Settings file changed. Reloading..."); + this.Load(); + this.OnConfigurationChange?.Invoke(null, null); + } + + private void Update(Settings source) { + this.AutomaticUpdatesDisabled = source.AutomaticUpdatesDisabled; + this.AutomaticUpdateURL = source.AutomaticUpdateURL; + } + } } diff --git a/ZitiUpgradeSentinel/Program.cs b/ZitiUpgradeSentinel/Program.cs index eebc87ad2..4d082d668 100644 --- a/ZitiUpgradeSentinel/Program.cs +++ b/ZitiUpgradeSentinel/Program.cs @@ -1,17 +1,17 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ using System; @@ -26,161 +26,161 @@ limitations under the License. using System.Text; class FileWatcher { - private static string processName = Process.GetCurrentProcess().ProcessName; - private static string tempDir = $"{Environment.GetEnvironmentVariable("TEMP")}"; - private static string fileName = $"{processName}_{DateTime.Now:yyyyMMddHHmmss}.log"; - private static string logFilePath = Path.Combine(tempDir, fileName); - - public static async Task Main(string[] args) { - Log($"{processName} started"); - try { - if (Process.GetProcessesByName(processName).Length > 1) { - Log("Another instance is already running. Exiting..."); - return; - } - await RunWithTimeout(task: WaitForStartupChange(), timeout: TimeSpan.FromMinutes(5)); - StartZitiDesktopEdgeUI(); - } catch (Exception e) { - Log($"{processName} completed exceptionally: "); - Log(e.ToString()); - } finally { - Log($"{processName} completed"); - } - } - - public static DateTime GetCurrentStartTime(StreamWriter writer, StreamReader reader) { - string statusCommand = "{\"Command\":\"Status\"}"; - writer.WriteLine(statusCommand); - writer.Flush(); - var statusResponse = reader.ReadLine(); - DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Response)); - MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(statusResponse)); - Response response = (Response)serializer.ReadObject(ms); - - DateTime startTime = DateTime.Parse(response.Data.StartTime).ToLocalTime(); - Log($"StartTime: {startTime}"); - return startTime; - throw new Exception("could not obtain current time from data service"); - } - - public static async Task WaitForStartupChange() { - DateTime startTime = DateTime.Now; - try { - using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "ziti-edge-tunnel.sock", PipeDirection.InOut)) { - pipeClient.Connect(); - StreamWriter writer = new StreamWriter(pipeClient); - StreamReader reader = new StreamReader(pipeClient); - - try { - startTime = GetCurrentStartTime(writer, reader); - Log($"initial start time {startTime}"); - } catch { - Log("Could not obtain current time. The service is expected to be down. Using 'now' as current time."); - } - } - } catch (Exception ex) { - Log($"Error: {ex.Message}"); - } - - while (true) { - try { - using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "ziti-edge-tunnel.sock", PipeDirection.InOut)) { - pipeClient.Connect(); - StreamWriter writer = new StreamWriter(pipeClient); - StreamReader reader = new StreamReader(pipeClient); - DateTime nextStartTime = GetCurrentStartTime(writer, reader); - if (startTime == nextStartTime) { - Log($"{startTime} is equal to {nextStartTime}"); - } else { - Log($"{startTime} has changed to {nextStartTime}"); - return; - } - await Task.Delay(TimeSpan.FromMilliseconds(500)); // wait for the serice to start and return a result - } - } catch (Exception ex) { - Log($"Error: {ex.Message}"); - } - await Task.Delay(TimeSpan.FromMilliseconds(500)); // try again... - } - } - - public static async Task RunWithTimeout(Task task, TimeSpan timeout) { - using (var cancellationTokenSource = new CancellationTokenSource()) { - if (await Task.WhenAny(task, Task.Delay(timeout)) == task) { - cancellationTokenSource.Cancel(); - await task; - } else { - throw new TimeoutException("The operation has timed out."); - } - } - } - public static void Log(string message) { - Console.WriteLine(message); - File.AppendAllText(logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}\n"); - } - - public static void StartZitiDesktopEdgeUI() { - Log($"trying to find the UI to start"); - string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); - - List dirs = new List(Directory.EnumerateDirectories(programFilesX86, "NetFoundry*")); - - if (dirs.Count > 1) { - Log($"cannot start the UI. too many directories to search??? Found: {dirs.Count} {string.Join(",", dirs)}"); - } else if (dirs.Count < 1) { - Log($"cannot start the UI. No ZitiDesktopEdge.exe found"); - } else { - var zitiFiles = Directory.GetFiles(dirs[0], "ZitiDesktopEdge.exe", SearchOption.AllDirectories); - - foreach (var file in zitiFiles) { - Console.WriteLine($"Found ZitiDesktopEdge at: {file}"); - using (Process process = new Process()) { - process.StartInfo.FileName = file; - process.StartInfo.Arguments = "version"; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - process.Start(); - Log($"Started {file}"); - } - } - } - } + private static string processName = Process.GetCurrentProcess().ProcessName; + private static string tempDir = $"{Environment.GetEnvironmentVariable("TEMP")}"; + private static string fileName = $"{processName}_{DateTime.Now:yyyyMMddHHmmss}.log"; + private static string logFilePath = Path.Combine(tempDir, fileName); + + public static async Task Main(string[] args) { + Log($"{processName} started"); + try { + if (Process.GetProcessesByName(processName).Length > 1) { + Log("Another instance is already running. Exiting..."); + return; + } + await RunWithTimeout(task: WaitForStartupChange(), timeout: TimeSpan.FromMinutes(5)); + StartZitiDesktopEdgeUI(); + } catch (Exception e) { + Log($"{processName} completed exceptionally: "); + Log(e.ToString()); + } finally { + Log($"{processName} completed"); + } + } + + public static DateTime GetCurrentStartTime(StreamWriter writer, StreamReader reader) { + string statusCommand = "{\"Command\":\"Status\"}"; + writer.WriteLine(statusCommand); + writer.Flush(); + var statusResponse = reader.ReadLine(); + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Response)); + MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(statusResponse)); + Response response = (Response)serializer.ReadObject(ms); + + DateTime startTime = DateTime.Parse(response.Data.StartTime).ToLocalTime(); + Log($"StartTime: {startTime}"); + return startTime; + throw new Exception("could not obtain current time from data service"); + } + + public static async Task WaitForStartupChange() { + DateTime startTime = DateTime.Now; + try { + using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "ziti-edge-tunnel.sock", PipeDirection.InOut)) { + pipeClient.Connect(); + StreamWriter writer = new StreamWriter(pipeClient); + StreamReader reader = new StreamReader(pipeClient); + + try { + startTime = GetCurrentStartTime(writer, reader); + Log($"initial start time {startTime}"); + } catch { + Log("Could not obtain current time. The service is expected to be down. Using 'now' as current time."); + } + } + } catch (Exception ex) { + Log($"Error: {ex.Message}"); + } + + while (true) { + try { + using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "ziti-edge-tunnel.sock", PipeDirection.InOut)) { + pipeClient.Connect(); + StreamWriter writer = new StreamWriter(pipeClient); + StreamReader reader = new StreamReader(pipeClient); + DateTime nextStartTime = GetCurrentStartTime(writer, reader); + if (startTime == nextStartTime) { + Log($"{startTime} is equal to {nextStartTime}"); + } else { + Log($"{startTime} has changed to {nextStartTime}"); + return; + } + await Task.Delay(TimeSpan.FromMilliseconds(500)); // wait for the serice to start and return a result + } + } catch (Exception ex) { + Log($"Error: {ex.Message}"); + } + await Task.Delay(TimeSpan.FromMilliseconds(500)); // try again... + } + } + + public static async Task RunWithTimeout(Task task, TimeSpan timeout) { + using (var cancellationTokenSource = new CancellationTokenSource()) { + if (await Task.WhenAny(task, Task.Delay(timeout)) == task) { + cancellationTokenSource.Cancel(); + await task; + } else { + throw new TimeoutException("The operation has timed out."); + } + } + } + public static void Log(string message) { + Console.WriteLine(message); + File.AppendAllText(logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}\n"); + } + + public static void StartZitiDesktopEdgeUI() { + Log($"trying to find the UI to start"); + string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + + List dirs = new List(Directory.EnumerateDirectories(programFilesX86, "NetFoundry*")); + + if (dirs.Count > 1) { + Log($"cannot start the UI. too many directories to search??? Found: {dirs.Count} {string.Join(",", dirs)}"); + } else if (dirs.Count < 1) { + Log($"cannot start the UI. No ZitiDesktopEdge.exe found"); + } else { + var zitiFiles = Directory.GetFiles(dirs[0], "ZitiDesktopEdge.exe", SearchOption.AllDirectories); + + foreach (var file in zitiFiles) { + Console.WriteLine($"Found ZitiDesktopEdge at: {file}"); + using (Process process = new Process()) { + process.StartInfo.FileName = file; + process.StartInfo.Arguments = "version"; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.Start(); + Log($"Started {file}"); + } + } + } + } } [DataContract] public class Response { - [DataMember] - public bool Success { get; set; } + [DataMember] + public bool Success { get; set; } - [DataMember] - public Data Data { get; set; } + [DataMember] + public Data Data { get; set; } - [DataMember] - public int Code { get; set; } + [DataMember] + public int Code { get; set; } } [DataContract] public class Data { - [DataMember] - public bool Active { get; set; } + [DataMember] + public bool Active { get; set; } - [DataMember] - public long Duration { get; set; } + [DataMember] + public long Duration { get; set; } - [DataMember] - public string StartTime { get; set; } + [DataMember] + public string StartTime { get; set; } - [DataMember] - public List Identities { get; set; } + [DataMember] + public List Identities { get; set; } } [DataContract] public class Identity { - [DataMember] - public string Name { get; set; } + [DataMember] + public string Name { get; set; } - [DataMember] - public string Identifier { get; set; } + [DataMember] + public string Identifier { get; set; } } \ No newline at end of file diff --git a/ZitiUpgradeSentinel/Properties/AssemblyInfo.cs b/ZitiUpgradeSentinel/Properties/AssemblyInfo.cs index 385479198..9344d2610 100644 --- a/ZitiUpgradeSentinel/Properties/AssemblyInfo.cs +++ b/ZitiUpgradeSentinel/Properties/AssemblyInfo.cs @@ -1,20 +1,20 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System.Reflection; +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/build-test-release.ps1 b/build-test-release.ps1 index a33b04745..ae6d0fe2f 100644 --- a/build-test-release.ps1 +++ b/build-test-release.ps1 @@ -16,8 +16,6 @@ param( [string]$versionQualifier = "" ) echo "" -$env:ZITI_DESKTOP_EDGE_DOWNLOAD_URL="$url" -$env:ZITI_DESKTOP_EDGE_VERSION="$version" $scriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Path -Parent $outputPath = "$scriptDirectory\release-streams\${version}.json" diff --git a/release-notes.md b/release-notes.md index 5d7990080..d976cd103 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,3 +1,119 @@ +# Release 2.5.0.10 & 2.5.0.11 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* logging was overly verbose due to new healthchecking +* fixed log level setting + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* *ziti-tunneler: v2.0.0-alpha24 +* *ziti-sdk: 2.0.0-alpha23 +* *tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + + +# Release 2.5.0.9 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* logging was overly verbose due to new healthchecking + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha22 +* ziti-sdk: 2.0.0-alpha23 +* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + +# Release 2.5.0.8 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* logging was broken + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha21 +* ziti-sdk: 2.0.0-alpha23 +* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + +# Release 2.5.0.7 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* n/a + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha20 +* ziti-sdk: 2.0.0-alpha22 +* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + + +# Release 2.5.0.6 + +## What's New +* Added stalled ziti-edge-tunnel detection. If the process doesn't respond for 15 seconds the monitor service will + administratively terminate the process. Example log output shown below: + + [2024-09-17T22:27:20.980Z] INFO ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check ends successfully + [2024-09-17T22:27:35.974Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 1 times + [2024-09-17T22:27:40.975Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 2 times + [2024-09-17T22:27:45.975Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 3 times + [2024-09-17T22:27:45.975Z] WARN ZitiUpdateService.UpdateService forcefully stopping ziti-edge-tunnel as it has been blocked for too long + [2024-09-17T22:27:45.975Z] INFO ZitiUpdateService.UpdateService Closing the "data service [ziti]" process + [2024-09-17T22:27:45.975Z] INFO ZitiUpdateService.UpdateService Killing: System.Diagnostics.Process (ziti-edge-tunnel) + +## Other changes: +* n/a + +## Bugs fixed: +* n/a + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha19 +* *ziti-sdk: 2.0.0-alpha21 +* *tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + +# Release 2.5.0.3 + +## What's New +* none + +## Other changes: +* added debug option to show when the data channel closes unexpectedly + +## Bugs fixed: +* n/a + +## Dependency Updates +* ziti-tunnel-sdk-c v2.0.0-alpha11/c sdk 2.0.0-alpha8 + # Release 2.5.0.2 ## What's New diff --git a/release-streams/ctrlha-alpha.json b/release-streams/ctrlha-alpha.json index b4abcaec4..9ae079d10 100644 --- a/release-streams/ctrlha-alpha.json +++ b/release-streams/ctrlha-alpha.json @@ -1,12 +1,12 @@ { - "name": "2.5.0.2", - "tag_name": "2.5.0.2", - "published_at": "2024-07-25T11:10:53Z", + "name": "2.5.0.11", + "tag_name": "2.5.0.11", + "published_at": "2024-09-27T17:55:00Z", "installation_critical": false, "assets": [ { - "name": "Ziti.Desktop.Edge.Client-2.5.0.2.exe", - "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.5.0.0-alpha/Ziti.Desktop.Edge.Client-2.5.0.2.exe" + "name": "Ziti.Desktop.Edge.Client-2.5.0.11.exe", + "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.5.0.11/Ziti.Desktop.Edge.Client-2.5.0.11.exe" } ] } diff --git a/release-streams/latest.json b/release-streams/latest.json index 222b0db02..da74e2e3c 100644 --- a/release-streams/latest.json +++ b/release-streams/latest.json @@ -1,12 +1,12 @@ { - "name": "2.4.0.0", - "tag_name": "2.4.0.0", - "published_at": "2024-07-11T12:00:00Z", + "name": "2.4.1.0", + "tag_name": "2.4.1.0", + "published_at": "2024-09-23T05:50:16Z", "installation_critical": false, "assets": [ { - "name": "Ziti.Desktop.Edge.Client-2.4.0.0.exe", - "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.4.0.0/Ziti.Desktop.Edge.Client-2.4.0.0.exe" + "name": "Ziti.Desktop.Edge.Client-2.4.1.0.exe", + "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.4.1.0/Ziti.Desktop.Edge.Client-2.4.1.0.exe" } ] } diff --git a/release-streams/stable.json b/release-streams/stable.json index 4468be85a..e6a61cbda 100644 --- a/release-streams/stable.json +++ b/release-streams/stable.json @@ -1,12 +1,12 @@ { - "name": "2.4.0.0", - "tag_name": "2.4.0.0", - "published_at": "2024-07-24T12:00:00Z", + "name": "2.4.1.0", + "tag_name": "2.4.1.0", + "published_at": "2024-10-07T10:50:00Z", "installation_critical": false, "assets": [ { - "name": "Ziti.Desktop.Edge.Client-2.4.0.0.exe", - "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.4.0.0/Ziti.Desktop.Edge.Client-2.4.0.0.exe" + "name": "Ziti.Desktop.Edge.Client-2.4.1.0.exe", + "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.4.1.0/Ziti.Desktop.Edge.Client-2.4.1.0.exe" } ] } diff --git a/setup-mfa-test.ps1 b/setup-mfa-test.ps1 index bb2b67713..6104a68bf 100644 --- a/setup-mfa-test.ps1 +++ b/setup-mfa-test.ps1 @@ -13,7 +13,6 @@ if ("$env:CLEAR_IDENTITIES_OK" -ne "yes") { Remove-Item C:\Windows\System32\config\systemprofile\AppData\Roaming\NetFoundry\mfa*.json Remove-Item C:\Windows\System32\config\systemprofile\AppData\Roaming\NetFoundry\config*.json Remove-Item "$env:APPDATA\NetFoundry\*.json" -#copy C:\Users\clint\AppData\Roaming\NetFoundry\empty.config.json C:\Users\clint\AppData\Roaming\NetFoundry\config.json echo "starting reset" @@ -75,16 +74,6 @@ ziti edge verify ca $caName --cert $verificationCert $authPolicy=(ziti edge create auth-policy yubi-mfa --primary-cert-allowed --secondary-req-totp --primary-cert-expired-allowed) -$newUser="clint" -ziti pki create client --pki-root "${zitiPkiRoot}" --ca-name "$caName" --client-file "$newUser" --client-name "$newUser" -$newUserCert=(Get-ChildItem -Path $zitiPkiRoot -Filter "$newUser.cert" -Recurse).FullName -$newUserKey=(Get-ChildItem -Path $zitiPkiRoot -Filter "$newUser.key" -Recurse).FullName -ziti edge create identity $newUser --auth-policy "$authPolicy" -ziti edge create enrollment ottca $newUser $caName - -$ottcajwt = (ziti edge list identities "name contains \""$newUser\""" -j | ConvertFrom-Json).data.enrollment.ottca.jwt -Set-Content -Path "$newUser.jwt" -Value $ottcajwt -NoNewline -Encoding ASCII - $count = 0 $iterations = 10 for ($i = 0; $i -lt $iterations; $i++) { @@ -162,6 +151,70 @@ ziti edge update service-policy "$name.svc.0.ziti.dial" --posture-check-roles "@ +$caName="3rd-party-ca-"+(Get-Date).ToString("yyyy-MM-dd-HH-mm-ss") +clear + + + + + + + + + + + + + + +$zitiUser="admin" +$zitiPwd="admin" +$zitiCtrl="localhost:1280" +ziti edge login $zitiCtrl -u $zitiUser -p $zitiPwd -y + +$caName="my-ca" +$newUser="$caName-user" +$zitiPkiRoot="C:\temp\support\discourse\2790\$caName\pki" +ziti pki create ca --pki-root "${zitiPkiRoot}" --ca-file "$caName" + +$rootCa=(Get-ChildItem -Path $zitiPkiRoot -Filter "$caName.cert" -Recurse).FullName +"root ca path: $rootCa" + +ziti edge create ca "$caName" "$rootCa" --auth --ottca + +$verificationToken=((ziti edge list cas -j | ConvertFrom-Json).data | Where-Object { $_.name -eq $caName }[0]).verificationToken +ziti pki create client --pki-root "${zitiPkiRoot}" --ca-name "$caName" --client-file "$verificationToken" --client-name "$verificationToken" + +$verificationCert=(Get-ChildItem -Path $zitiPkiRoot -Filter "$verificationToken.cert" -Recurse).FullName +"verification cert path: $verificationCert" +ziti edge verify ca $caName --cert $verificationCert + + +$authPolicy=(ziti edge create auth-policy "$caName-auth-policy" --primary-cert-allowed --secondary-req-totp --primary-cert-expired-allowed) + + +ziti pki create client --pki-root "${zitiPkiRoot}" --ca-name "$caName" --client-file "$newUser" --client-name "$newUser" + +$newUserCert=(Get-ChildItem -Path $zitiPkiRoot -Filter "$newUser.cert" -Recurse).FullName +$newUserKey=(Get-ChildItem -Path $zitiPkiRoot -Filter "$newUser.key" -Recurse).FullName + +ziti edge create identity $newUser --auth-policy "$authPolicy" +ziti edge create enrollment ottca $newUser $caName + +if ($PSVersionTable.PSVersion.Major -gt 5) { #powershell.... + $ottcajwt = (ziti edge list identities "name contains ""$newUser""" -j | ConvertFrom-Json).data.enrollment.ottca.jwt +} else { + $ottcajwt = (ziti edge list identities "name contains \""$newUser\""" -j | ConvertFrom-Json).data.enrollment.ottca.jwt +} + +Set-Content -Path "$zitiPkiRoot\$newUser.jwt" -Value $ottcajwt -NoNewline -Encoding ASCII + +& 'C:\Program Files (x86)\NetFoundry Inc\Ziti Desktop Edge\ziti-edge-tunnel.exe' ` + enroll ` + --jwt "$zitiPkiRoot\$newUser.jwt" ` + --cert $newUserCert ` + --key $newUserKey ` + --identity "C:\Windows\System32\config\systemprofile\AppData\Roaming\NetFoundry\${newUser}.json" diff --git a/update-versions.ps1 b/update-versions.ps1 index 3d8bf7259..7dc7d3c6b 100644 --- a/update-versions.ps1 +++ b/update-versions.ps1 @@ -32,14 +32,14 @@ $assemblyInfo="./DesktopEdge/Properties/AssemblyInfo.cs" $assemblyInfoReplaced="${assemblyInfo}.replaced" echo "Replacing version in $assemblyInfo into $assemblyInfoReplaced" (Get-Content -Encoding UTF8 -path $assemblyInfo -Raw) -replace 'Version\("[0-9]*.[0-9]*.[0-9]*.[0-9]*', "Version(""${v}" | Set-Content -Encoding UTF8 -Path "$assemblyInfoReplaced" -NoNewline -rm $assemblyInfo -mv $assemblyInfoReplaced $assemblyInfo +Remove-Item $assemblyInfo +Move-Item $assemblyInfoReplaced $assemblyInfo $assemblyInfo="./ZitiUpdateService/Properties/AssemblyInfo.cs" $assemblyInfoReplaced="${assemblyInfo}.replaced" echo "Replacing version in $assemblyInfo into $assemblyInfoReplaced" (Get-Content -Encoding UTF8 -path $assemblyInfo -Raw) -replace 'Version\("[0-9]*.[0-9]*.[0-9]*.[0-9]*', "Version(""${v}" | Set-Content -Encoding UTF8 -Path "$assemblyInfoReplaced" -NoNewline -rm $assemblyInfo -mv $assemblyInfoReplaced $assemblyInfo +Remove-Item $assemblyInfo +Move-Item $assemblyInfoReplaced $assemblyInfo echo "==================================== update-versions.ps1 complete ====================================" From 3f6c61ab28db50a063bea4af2449be8454e89273 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:57:28 -0400 Subject: [PATCH 07/12] build updates --- Installer/build.ps1 | 2 +- adv-inst-version | 2 +- release-notes.md | 138 ++------------------------------------------ version | 2 +- 4 files changed, 9 insertions(+), 135 deletions(-) diff --git a/Installer/build.ps1 b/Installer/build.ps1 index 23347a831..084b8a622 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -27,7 +27,7 @@ $ADV_INST_HOME = "C:\Program Files (x86)\Caphyon\Advanced Installer ${ADV_INST_V $SIGNTOOL="${ADV_INST_HOME}\third-party\winsdk\x64\signtool.exe" $ADVINST = "${ADV_INST_HOME}\bin\x86\AdvancedInstaller.com" $ADVPROJECT = "${scriptPath}\ZitiDesktopEdge.aip" -$ZITI_EDGE_TUNNEL_VERSION="v1.1.4" +$ZITI_EDGE_TUNNEL_VERSION="v1.2.2" echo "Cleaning previous build folder if it exists" Remove-Item "${buildPath}" -r -ErrorAction Ignore diff --git a/adv-inst-version b/adv-inst-version index b798090d4..70a91e23e 100644 --- a/adv-inst-version +++ b/adv-inst-version @@ -1 +1 @@ -21.8.2 +22.1 diff --git a/release-notes.md b/release-notes.md index d976cd103..12afc61a2 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,146 +1,20 @@ -# Release 2.5.0.10 & 2.5.0.11 +# Release 2.5.0.0 ## What's New -* n/a +* nothing yet -## Other changes: -* n/a - -## Bugs fixed: -* logging was overly verbose due to new healthchecking -* fixed log level setting - -## Dependency Updates - -ziti-edge-tunnel.exe version -v: -* *ziti-tunneler: v2.0.0-alpha24 -* *ziti-sdk: 2.0.0-alpha23 -* *tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] - - -# Release 2.5.0.9 - -## What's New -* n/a - -## Other changes: -* n/a - -## Bugs fixed: -* logging was overly verbose due to new healthchecking - -## Dependency Updates - -ziti-edge-tunnel.exe version -v: -* ziti-tunneler: v2.0.0-alpha22 -* ziti-sdk: 2.0.0-alpha23 -* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] - -# Release 2.5.0.8 - -## What's New -* n/a - -## Other changes: -* n/a - -## Bugs fixed: -* logging was broken - -## Dependency Updates - -ziti-edge-tunnel.exe version -v: -* ziti-tunneler: v2.0.0-alpha21 -* ziti-sdk: 2.0.0-alpha23 -* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] - -# Release 2.5.0.7 - -## What's New -* n/a - -## Other changes: -* n/a - -## Bugs fixed: -* n/a - -## Dependency Updates - -ziti-edge-tunnel.exe version -v: -* ziti-tunneler: v2.0.0-alpha20 -* ziti-sdk: 2.0.0-alpha22 -* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] - - -# Release 2.5.0.6 - -## What's New -* Added stalled ziti-edge-tunnel detection. If the process doesn't respond for 15 seconds the monitor service will - administratively terminate the process. Example log output shown below: - - [2024-09-17T22:27:20.980Z] INFO ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check ends successfully - [2024-09-17T22:27:35.974Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 1 times - [2024-09-17T22:27:40.975Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 2 times - [2024-09-17T22:27:45.975Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 3 times - [2024-09-17T22:27:45.975Z] WARN ZitiUpdateService.UpdateService forcefully stopping ziti-edge-tunnel as it has been blocked for too long - [2024-09-17T22:27:45.975Z] INFO ZitiUpdateService.UpdateService Closing the "data service [ziti]" process - [2024-09-17T22:27:45.975Z] INFO ZitiUpdateService.UpdateService Killing: System.Diagnostics.Process (ziti-edge-tunnel) - -## Other changes: -* n/a - -## Bugs fixed: -* n/a - -## Dependency Updates - -ziti-edge-tunnel.exe version -v: -* ziti-tunneler: v2.0.0-alpha19 -* *ziti-sdk: 2.0.0-alpha21 -* *tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] - -# Release 2.5.0.3 - -## What's New +## Other changes * none -## Other changes: -* added debug option to show when the data channel closes unexpectedly - ## Bugs fixed: -* n/a - -## Dependency Updates -* ziti-tunnel-sdk-c v2.0.0-alpha11/c sdk 2.0.0-alpha8 - -# Release 2.5.0.2 - -## What's New -* updated c-sdk/tunneler to work with HA controllers - -## Other changes: * none -## Bugs fixed: -* n/a - ## Dependency Updates -* ziti-tunnel-sdk-c v2.0.0-alpha10/c sdk 2.0.0-alpha8 - -# Release 2.5.0.1 -## What's New -* updated c-sdk/tunneler to work with HA controllers +* ziti-tunneler: v1.2.2 +* ziti-sdk: 1.1.2 +* tlsuv: v0.32.6[OpenSSL 3.3.1 4 Jun 2024] -## Other changes: -* none - -## Bugs fixed: -* n/a - -## Dependency Updates -* ziti-tunnel-sdk-c v2.0.0-alpha9/c sdk 2.0.0-alpha6 # Release 2.4.0.1 diff --git a/version b/version index 42dece39c..e975051fc 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.4.1.0 +2.5.0.0 From aa4410737a871ae00cce4978dec9dd20efa1f67e Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:29:54 -0400 Subject: [PATCH 08/12] build script improvements --- Installer/build.ps1 | 73 +++++++++++++++++++++++++++++++----------- build-test-release.ps1 | 44 +++++++++++++++++++++++-- update-versions.ps1 | 22 +++++++++---- 3 files changed, 110 insertions(+), 29 deletions(-) diff --git a/Installer/build.ps1 b/Installer/build.ps1 index 084b8a622..f07d53d42 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -17,6 +17,7 @@ function verifyFile($path) { } } +echo "" echo "========================== build.ps1 begins ==========================" $invocation = (Get-Variable MyInvocation).Value $scriptPath = Split-Path $invocation.MyCommand.Path @@ -35,35 +36,67 @@ mkdir "${buildPath}" -ErrorAction Ignore > $null $global:ProgressPreference = "SilentlyContinue" $destination="${scriptPath}\zet.zip" - +$unzip = $true if($null -eq $env:ZITI_EDGE_TUNNEL_BUILD) { if($null -eq $env:ZITI_EDGE_TUNNEL_VERSION) { # use the default $ZITI_EDGE_TUNNEL_VERSION } else { $ZITI_EDGE_TUNNEL_VERSION=$env:ZITI_EDGE_TUNNEL_VERSION } - echo "========================== fetching ziti-edge-tunnel ==========================" - $zet_dl="https://github.com/openziti/ziti-tunnel-sdk-c/releases/download/${ZITI_EDGE_TUNNEL_VERSION}/ziti-edge-tunnel-Windows_x86_64.zip" - echo "Beginning to download ziti-edge-tunnel from ${zet_dl} to ${destination}" - echo "" - $response = Invoke-WebRequest $zet_dl -OutFile "${destination}" + if (Test-Path ${destination} -PathType Container) { + echo "========================== fetching ziti-edge-tunnel ==========================" + $zet_dl="https://github.com/openziti/ziti-tunnel-sdk-c/releases/download/${ZITI_EDGE_TUNNEL_VERSION}/ziti-edge-tunnel-Windows_x86_64.zip" + echo "Beginning to download ziti-edge-tunnel from ${zet_dl} to ${destination}" + echo "" + $response = Invoke-WebRequest $zet_dl -OutFile "${destination}" + } else { + Write-Host -ForegroundColor Yellow "ziti-edge-tunnel.zip exists and won't be downloaded again: ${destination}" + } } else { echo "========================== using locally defined ziti-edge-tunnel ==========================" $zet_dl="${env:ZITI_EDGE_TUNNEL_BUILD}" - echo "Sourcing ziti-edge-tunnel from ${zet_dl}" + echo "Using ziti-edge-tunnel declared from ${zet_dl}" echo "" if ($SourcePath -match "^https?://") { $response = Invoke-WebRequest -Uri "${zet_dl}" -OutFile "${destination}" } else { - $response = Copy-Item -Path "${zet_dl}" -Destination "${destination}" -ErrorAction Stop + echo "Determining if the location is a directory or zip file" + if (Test-Path $zet_dl -PathType Container) { + $unzip = $false + } elseif ($zet_dl -match '\.zip$') { + echo "Copying zip file to destination" + echo " FROM: ${zet_dl}" + echo " TO: ${destination}" + $response = Copy-Item -Path "${zet_dl}" -Destination "${destination}" -ErrorAction Stop + } else { + Write-Host -ForegroundColor Red "Unknown type. Expected either a .zip file or a directory:" + Write-Host -ForegroundColor Red " - ${zet_dl}" + exit 1 + } } } -verifyFile("${destination}") -echo "Expanding downloaded file..." -Expand-Archive -Path "${destination}" -Force -DestinationPath "${buildPath}\service" -echo "expanded ${destination} file to ${buildPath}\service" +if($unzip) { + verifyFile("${destination}") + echo "Expanding downloaded file..." + Expand-Archive -Path "${destination}" -Force -DestinationPath "${buildPath}\service" + echo "expanded ${destination} file to ${buildPath}\service" +} else { + if (Test-Path -Path "${buildPath}\service") { + echo "removing old service folder at: ${buildPath}\service" + Remove-Item -Path "${buildPath}\service" -Recurse -Force -ErrorAction SilentlyContinue + } + + echo "creating new service directory: ${buildPath}\service" + New-Item -Path "${buildPath}\service" -ItemType Directory | Out-Null + + echo "Copying files from directory to destination" + echo " FROM: ${zet_dl}\*" + echo " TO: ${buildPath}\service\" + $response = Copy-Item -Path "${zet_dl}\wintun.dll" -Destination "${buildPath}\service\wintun.dll" -ErrorAction Stop -Force + $response = Copy-Item -Path "${zet_dl}\ziti-edge-tunnel.exe" -Destination "${buildPath}\service\ziti-edge-tunnel.exe" -ErrorAction Stop -Force +} echo "========================== building and moving the custom signing tool ==========================" dotnet build -c Release "${checkoutRoot}/AWSSigner.NET\AWSSigner.NET.csproj" @@ -75,8 +108,12 @@ $env:SIGNTOOL_PATH="${SIGNTOOL}" Push-Location ${checkoutRoot} +if ($version -eq "") { + $version=(Get-Content -Path ${checkoutRoot}\version) +} + echo "Updating the version for UI and Installer" -.\update-versions.ps1 +.\update-versions.ps1 $version echo "Restoring the .NET project" nuget restore .\ZitiDesktopEdge.sln @@ -86,10 +123,6 @@ msbuild ZitiDesktopEdge.sln /property:Configuration=Release Pop-Location -if ($version -eq "") { - $version=(Get-Content -Path ${checkoutRoot}\version) -} - echo "Building VERSION $version" if($null -ne $env:ZITI_DESKTOP_EDGE_VERSION) { @@ -122,7 +155,7 @@ $exeAbsPath="${outputPath}\${exeName}" if($null -eq $env:AWS_KEY_ID) { echo "" - echo "AWS_KEY_ID not set. __THE BINARY WILL NOT BE SIGNED!__" + echo "AWS_KEY_ID not set. __THE BINARY WILL NOT BE SIGNED!__" echo "" } @@ -137,7 +170,6 @@ if($null -eq $env:OPENZITI_P12_PASS_2024) { } (Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${version}.exe.sha256" -echo "========================== build.ps1 completed ==========================" $outputPath = "${scriptPath}\Output\Ziti Desktop Edge Client-${version}.exe.json" & .\Installer\output-build-json.ps1 -version $version -url $url -stream $stream -published_at $published_at -outputPath $outputPath -versionQualifier $versionQualifier @@ -152,3 +184,6 @@ copy $outputPath "$checkoutRoot\release-streams\beta.json" if($revertGitAfter) { git checkout DesktopEdge/Properties/AssemblyInfo.cs ZitiUpdateService/Properties/AssemblyInfo.cs Installer/ZitiDesktopEdge.aip } + + +echo "========================== build.ps1 completed ==========================" diff --git a/build-test-release.ps1 b/build-test-release.ps1 index ae6d0fe2f..7b6d96dec 100644 --- a/build-test-release.ps1 +++ b/build-test-release.ps1 @@ -6,7 +6,6 @@ # .\build-test-release.ps1 -version 1.2.3 -url https://lnxiskqx49x4.share.zrok.io/local -stream "dev" -published_at (Get-Date) # .\build-test-release.ps1 -version 1.2.3 -url https://lnxiskqx49x4.share.zrok.io/local -stream "dev" -published_at "2023-11-02T14:30:00" param( - [Parameter(Mandatory = $true)] [string]$version, [string]$url = "http://localhost:8000/release-streams/local", [string]$stream = "local", @@ -18,6 +17,44 @@ param( echo "" $scriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Path -Parent +$version = $version.Trim() +if (-not $version) { + if (Test-Path -Path "version") { + $version = (Get-Content -Path "version" -Raw).Trim() + Write-Host -NoNewline "Version not supplied. Using version from file and incrementing: " + Write-Host -ForegroundColor Yellow "${version}" + + # Increment the last tuple + $versionWithoutPrefix = $version -replace '^v', '' + $segments = $versionWithoutPrefix -split '\.' + $segments[-1] = [int]$segments[-1] + 1 + $version = ($segments -join '.') + + Write-Host -NoNewline "New Version: " + Write-Host -ForegroundColor Green "$version" + # Check if the 'version' file has changes in git + $gitStatus = git status --porcelain "version" + + if (-not $gitStatus) { + # File has not been modified in git, proceed to update it + Set-Content -Path "version" -Value $version -NoNewline + } else { + Write-Host "The version file has changes in git. Update skipped." -ForegroundColor Yellow + } + } else { + Write-Host "Version file not found" -ForegroundColor Red + exit 1 + } +} else { + # Regex to match semantic versioning pattern + if ($version -notmatch '^v?\d+(\.\d+){0,3}$') { + Write-Host -ForegroundColor Red "Invalid version format [${version}]. Expected a semantic version (e.g., 1.0.0)." + exit 1 + } + Write-Host -NoNewline "Version: " + Write-Host -ForegroundColor Green "$version" +} + $outputPath = "$scriptDirectory\release-streams\${version}.json" & .\Installer\output-build-json.ps1 -version $version -url $url -stream $stream -published_at $published_at -outputPath $outputPath @@ -28,7 +65,8 @@ if(! $jsonOnly) { & .\Installer\build.ps1 -version $version -url $url -stream $stream -published_at $published_at -jsonOnly $jsonOnly -revertGitAfter $revertGitAfter -versionQualifier $versionQualifier $exitCode = $LASTEXITCODE if($exitCode -gt 0) { - Write-Host "build.ps1 failed!" + Write-Host -ForegroundColor Red "ERROR:" + Write-Host -ForegroundColor Red " - build.ps1 failed!" exit $exitCode } @@ -56,4 +94,4 @@ Write-Host "" Write-Host " python -m http.server 8000" Write-Host "" Write-Host "Set the automatic upgrade url to http://localhost:8000/release-streams/local.json" -Write-Host "" +Write-Host "" \ No newline at end of file diff --git a/update-versions.ps1 b/update-versions.ps1 index 7dc7d3c6b..71a17bafe 100644 --- a/update-versions.ps1 +++ b/update-versions.ps1 @@ -1,3 +1,7 @@ +param( + [string]$version +) + function NormalizeVersion([System.Version] $v) { $major = $v.Major $minor = $v.Minor @@ -15,13 +19,17 @@ function NormalizeVersion([System.Version] $v) { } echo "==================================== update-versions.ps1 begins ====================================" -echo "Obtaining version information from .\version" -#$rawVersion=(Get-Content -Path .\version) -$installerVersion=(Get-Content -Path ${scriptPath}\..\version) -if($null -ne $env:ZITI_DESKTOP_EDGE_VERSION) { - echo "ZITI_DESKTOP_EDGE_VERSION is set. Using that: ${env:ZITI_DESKTOP_EDGE_VERSION} instead of version found in file ${installerVersion}" - $installerVersion=$env:ZITI_DESKTOP_EDGE_VERSION - echo "Version set to: ${installerVersion}" +if(-not $version.Trim()) { + echo "Obtaining version information from .\version" + #$rawVersion=(Get-Content -Path .\version) + $installerVersion=(Get-Content -Path ${scriptPath}\..\version) + if($null -ne $env:ZITI_DESKTOP_EDGE_VERSION) { + echo "ZITI_DESKTOP_EDGE_VERSION is set. Using that: ${env:ZITI_DESKTOP_EDGE_VERSION} instead of version found in file ${installerVersion}" + $installerVersion=$env:ZITI_DESKTOP_EDGE_VERSION + echo "Version set to: ${installerVersion}" + } +} else { + $installerVersion = $version } $v=NormalizeVersion($installerVersion) From fdf128d336d113bd422709acbd92b1594793af14 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:37:00 -0400 Subject: [PATCH 09/12] build improvement and beta url update --- Installer/build.ps1 | 8 +++++--- release-streams/beta.json | 10 +++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Installer/build.ps1 b/Installer/build.ps1 index f07d53d42..24cd8bcbd 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -35,7 +35,9 @@ Remove-Item "${buildPath}" -r -ErrorAction Ignore mkdir "${buildPath}" -ErrorAction Ignore > $null $global:ProgressPreference = "SilentlyContinue" -$destination="${scriptPath}\zet.zip" +$zetDownloadLoc="${scriptPath}\build\zet" +mkdir "${zetDownloadLoc}" -ErrorAction Ignore > $null +$destination="${zetDownloadLoc}\${ZITI_EDGE_TUNNEL_VERSION}-zet.zip" $unzip = $true if($null -eq $env:ZITI_EDGE_TUNNEL_BUILD) { if($null -eq $env:ZITI_EDGE_TUNNEL_VERSION) { @@ -44,13 +46,13 @@ if($null -eq $env:ZITI_EDGE_TUNNEL_BUILD) { $ZITI_EDGE_TUNNEL_VERSION=$env:ZITI_EDGE_TUNNEL_VERSION } if (Test-Path ${destination} -PathType Container) { + Write-Host -ForegroundColor Yellow "ziti-edge-tunnel.zip exists and won't be downloaded again: ${destination}" + } else { echo "========================== fetching ziti-edge-tunnel ==========================" $zet_dl="https://github.com/openziti/ziti-tunnel-sdk-c/releases/download/${ZITI_EDGE_TUNNEL_VERSION}/ziti-edge-tunnel-Windows_x86_64.zip" echo "Beginning to download ziti-edge-tunnel from ${zet_dl} to ${destination}" echo "" $response = Invoke-WebRequest $zet_dl -OutFile "${destination}" - } else { - Write-Host -ForegroundColor Yellow "ziti-edge-tunnel.zip exists and won't be downloaded again: ${destination}" } } else { echo "========================== using locally defined ziti-edge-tunnel ==========================" diff --git a/release-streams/beta.json b/release-streams/beta.json index da74e2e3c..5f6736c1a 100644 --- a/release-streams/beta.json +++ b/release-streams/beta.json @@ -1,12 +1,12 @@ { - "name": "2.4.1.0", - "tag_name": "2.4.1.0", - "published_at": "2024-09-23T05:50:16Z", + "name": "2.5.0.0", + "tag_name": "2.5.0.0", + "published_at": "2024-10-18T06:35:33Z", "installation_critical": false, "assets": [ { - "name": "Ziti.Desktop.Edge.Client-2.4.1.0.exe", - "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.4.1.0/Ziti.Desktop.Edge.Client-2.4.1.0.exe" + "name": "Ziti.Desktop.Edge.Client-2.5.0.0.exe", + "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.5.0.0/Ziti.Desktop.Edge.Client-2.5.0.0.exe" } ] } From 82d0ad411ced450370e954ec6eb58512497b1725 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:56:35 -0400 Subject: [PATCH 10/12] bring in other changes from add-ext-auth-misses --- DesktopEdge/MainWindow.xaml.cs | 6 +- DesktopEdge/Models/ZitiIdentity.cs | 8 +- .../Views/ItemRenderers/IdentityItem.xaml.cs | 40 +++-- ZitiDesktopEdge.sln | 1 + ZitiUpdateService/UpdateService.cs | 2 +- manual-testing.md | 28 +++ release-notes.md | 162 +++++++++++++++++- version | 2 +- 8 files changed, 231 insertions(+), 18 deletions(-) create mode 100644 manual-testing.md diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs index 746fecafc..5156da7d5 100644 --- a/DesktopEdge/MainWindow.xaml.cs +++ b/DesktopEdge/MainWindow.xaml.cs @@ -83,7 +83,7 @@ static MainWindow() { ThisAssemblyName = asm.GetName().Name; state = (ZDEWViewState)Application.Current.Properties["ZDEWViewState"]; #if DEBUG - ExecutionDirectory = @"C:\Program Files (x86)\NetFoundry, Inc\Ziti Desktop Edge"; + ExecutionDirectory = @"C:\Program Files (x86)\NetFoundry Inc\Ziti Desktop Edge"; #else ExecutionDirectory = Path.GetDirectoryName(asm.Location); #endif @@ -439,7 +439,7 @@ public MainWindow() { notifyIcon.BalloonTipClosed += NotifyIcon_BalloonTipClosed; notifyIcon.MouseClick += NotifyIcon_MouseClick; notifyIcon.ContextMenu = this.contextMenu; - + IdentityMenu.OnDetach += OnDetach; MainMenu.OnDetach += OnDetach; @@ -1880,4 +1880,4 @@ public bool CanExecute(object parameter) { public event EventHandler CanExecuteChanged; #pragma warning restore CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used } -} \ No newline at end of file +} diff --git a/DesktopEdge/Models/ZitiIdentity.cs b/DesktopEdge/Models/ZitiIdentity.cs index 29e40d72c..09f514c37 100644 --- a/DesktopEdge/Models/ZitiIdentity.cs +++ b/DesktopEdge/Models/ZitiIdentity.cs @@ -36,8 +36,7 @@ public class ZitiIdentity { public bool IsMFAEnabled { get; set; } public void MFADebug(string where) { - logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}"); - //logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}\n\tShowMFA\t : {ShowMFA}"); + logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}\n\tNeedsExtAuth : {NeedsExtAuth}"); } private bool mfaNeeded = false; @@ -140,6 +139,11 @@ public static ZitiIdentity FromClient(DataStructures.Identity id) { NeedsExtAuth = id.NeedsExtAuth, }; + if (zid.Name.Contains(@"\")) { + int pos = zid.Name.LastIndexOf(@"\"); + zid.Name = zid.Name.Substring(pos + 1); + } + #if DEBUG zid.MFADebug("002"); #endif diff --git a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs index 1a32f5936..af0e3e06b 100644 --- a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs @@ -20,19 +20,17 @@ limitations under the License. using System.Windows.Input; using ZitiDesktopEdge.Models; using ZitiDesktopEdge.ServiceClient; -using Microsoft.Toolkit.Uwp.Notifications; using NLog; using SWM = System.Windows.Media; -using Windows.UI.WebUI; -using Windows.Media.Protection.PlayReady; using ZitiDesktopEdge.DataStructures; using System.Diagnostics; +using System.Web.UI; namespace ZitiDesktopEdge { /// /// User Control to list Identities and give status /// - public partial class IdentityItem : UserControl { + public partial class IdentityItem : System.Windows.Controls.UserControl { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); public delegate void StatusChanged(bool attached); @@ -209,14 +207,20 @@ public void RefreshUI() { } } - if (_identity.NeedsExtAuth) { - //show ext auth + int idViewState = CalculateIdentityState(_identity); + + if (idViewState == 0) { + ExtAuthRequired.Visibility = Visibility.Collapsed; + MfaRequired.Visibility = Visibility.Collapsed; + ServiceCountArea.Visibility = Visibility.Visible; + } else if (idViewState % (int)IdentityStates.NeedsExtAuth == 0) { ExtAuthRequired.Visibility = Visibility.Visible; + MfaRequired.Visibility = Visibility.Collapsed; ServiceCountArea.Visibility = Visibility.Collapsed; - } else { - //hide ext auth + } else if (idViewState % (int)IdentityStates.NeedsMfa == 0) { ExtAuthRequired.Visibility = Visibility.Collapsed; - ServiceCountArea.Visibility = Visibility.Visible; + MfaRequired.Visibility = Visibility.Visible; + ServiceCountArea.Visibility = Visibility.Collapsed; } IdName.Content = _identity.Name; @@ -226,6 +230,22 @@ public void RefreshUI() { ToggleStatus.Content = ((ToggleSwitch.Enabled) ? "ENABLED" : "DISABLED"); } + private int CalculateIdentityState(ZitiIdentity id) { + int ret = 0; + if (id.NeedsExtAuth) { + ret += (int)IdentityStates.NeedsExtAuth; + } + if (id.IsMFANeeded) { + ret += (int)IdentityStates.NeedsMfa; + } + return ret; + } + + enum IdentityStates { + NeedsMfa = 1, + NeedsExtAuth = 2, + } + private void TimingTimerTick(object sender, EventArgs e) { available = _identity.Services.Count; GetMaxTimeout(); @@ -357,7 +377,7 @@ async private void CompleteExtAuth(object sender, MouseButtonEventArgs e) { try { DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; ExternalAuthLoginResponse resp = await client.ExternalAuthLogin(_identity.Identifier); - Console.WriteLine(resp.Data.url); + Console.WriteLine(resp.Data?.url); Process.Start(resp.Data.url); } catch (Exception ex) { logger.Error("unexpected error!", ex); diff --git a/ZitiDesktopEdge.sln b/ZitiDesktopEdge.sln index f6986126b..16d154071 100644 --- a/ZitiDesktopEdge.sln +++ b/ZitiDesktopEdge.sln @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution build-test-release.ps1 = build-test-release.ps1 Installer\build.ps1 = Installer\build.ps1 .github\workflows\installer.build.yml = .github\workflows\installer.build.yml + manual-testing.md = manual-testing.md .github\workflows\mattermost-ziti-webhook.yml = .github\workflows\mattermost-ziti-webhook.yml Installer\output-build-json.ps1 = Installer\output-build-json.ps1 README.md = README.md diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index fed183f08..2ce821d3b 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -642,7 +642,7 @@ protected override void OnSessionChange(SessionChangeDescription changeDescripti protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) { Logger.Info("ziti-monitor OnPowerEvent was called {0}", powerStatus); - if(powerStatus == PowerBroadcastStatus.Suspend) { + if (powerStatus == PowerBroadcastStatus.Suspend) { // when going to sleep, make sure the healthcheck is disabled or accounts for going to sleep disableHealthCheck(); } diff --git a/manual-testing.md b/manual-testing.md new file mode 100644 index 000000000..95766d853 --- /dev/null +++ b/manual-testing.md @@ -0,0 +1,28 @@ +# Manual Testing + +These are the tests you need to make sure to perform per release. + +## Test Cases + +- Identity with dial access to a single service +- Identity with bind access to a single service +- Identity with dial access to a service with by TOTP posture check +- Identity with dial access to a service with by TOTP + time-based posture check +- Identity with dial access to a service with by TOTP + on-wake locking posture check + +- Identity with dial access to a service with process-based posture check +- Identity with dial access to a service with by posture check +- Identity with dial access to a service with by posture check +- Identity with dial access to a service with by posture check +- Identity with dial access to a service with by posture check +- Identity with dial access to a service with by posture check +- Identity with dial access to a service with by posture check +- Identity with dial access to a service with by posture check +- Identity with dial access to a service with by posture check + + +- Identity marked as disabled, is disabled on restart of zet +- Identity marked as enabled is enabled on restart +- Identity with overlapping service definitions in two different networks +- +- Multiple ziti edge tunnels running at one time, intercepts are removed on clean shutdown \ No newline at end of file diff --git a/release-notes.md b/release-notes.md index 12afc61a2..066ab81d6 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,4 +1,4 @@ -# Release 2.5.0.0 +# Release 2.5.0.13 ## What's New * nothing yet @@ -15,6 +15,166 @@ * ziti-sdk: 1.1.2 * tlsuv: v0.32.6[OpenSSL 3.3.1 4 Jun 2024] +# Release 2.5.0.12 + +## What's New +* IdP Integration! +* Keychain integration and TPM now used! + +## Other changes: +* n/a + +## Bugs fixed: +* n/a + +## Dependency Updates +ziti-tunneler: v2.0.0-alpha24.11 +ziti-sdk: 2.0.0-alpha29 +tlsuv: v0.32.2.1[OpenSSL 3.3.1 4 Jun 2024] + +# Release 2.5.0.10 & 2.5.0.11 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* logging was overly verbose due to new healthchecking +* fixed log level setting + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* *ziti-tunneler: v2.0.0-alpha24 +* *ziti-sdk: 2.0.0-alpha23 +* *tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + + +# Release 2.5.0.9 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* logging was overly verbose due to new healthchecking + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha22 +* ziti-sdk: 2.0.0-alpha23 +* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + +# Release 2.5.0.8 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* logging was broken + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha21 +* ziti-sdk: 2.0.0-alpha23 +* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + +# Release 2.5.0.7 + +## What's New +* n/a + +## Other changes: +* n/a + +## Bugs fixed: +* n/a + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha20 +* ziti-sdk: 2.0.0-alpha22 +* tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + + +# Release 2.5.0.6 + +## What's New +* Added stalled ziti-edge-tunnel detection. If the process doesn't respond for 15 seconds the monitor service will + administratively terminate the process. Example log output shown below: + + [2024-09-17T22:27:20.980Z] INFO ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check ends successfully + [2024-09-17T22:27:35.974Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 1 times + [2024-09-17T22:27:40.975Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 2 times + [2024-09-17T22:27:45.975Z] WARN ZitiUpdateService.UpdateService ziti-edge-tunnel aliveness check appears blocked and has been for 3 times + [2024-09-17T22:27:45.975Z] WARN ZitiUpdateService.UpdateService forcefully stopping ziti-edge-tunnel as it has been blocked for too long + [2024-09-17T22:27:45.975Z] INFO ZitiUpdateService.UpdateService Closing the "data service [ziti]" process + [2024-09-17T22:27:45.975Z] INFO ZitiUpdateService.UpdateService Killing: System.Diagnostics.Process (ziti-edge-tunnel) + +## Other changes: +* n/a + +## Bugs fixed: +* n/a + +## Dependency Updates + +ziti-edge-tunnel.exe version -v: +* ziti-tunneler: v2.0.0-alpha19 +* *ziti-sdk: 2.0.0-alpha21 +* *tlsuv: v0.31.4[OpenSSL 3.3.1 4 Jun 2024] + +# Release 2.5.0.3 + +## What's New +* none + +## Other changes: +* added debug option to show when the data channel closes unexpectedly + +## Bugs fixed: +* n/a + +## Dependency Updates +* ziti-tunnel-sdk-c v2.0.0-alpha11/c sdk 2.0.0-alpha8 + +# Release 2.5.0.2 + +## What's New +* updated c-sdk/tunneler to work with HA controllers + +## Other changes: +* none + +## Bugs fixed: +* n/a + +## Dependency Updates +* ziti-tunnel-sdk-c v2.0.0-alpha10/c sdk 2.0.0-alpha8 + +# Release 2.5.0.1 + +## What's New +* updated c-sdk/tunneler to work with HA controllers + +## Other changes: +* none + +## Bugs fixed: +* n/a + +## Dependency Updates +* ziti-tunnel-sdk-c v2.0.0-alpha9/c sdk 2.0.0-alpha6 # Release 2.4.0.1 diff --git a/version b/version index e975051fc..c4f828de6 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.5.0.0 +2.5.0.13 From 9b88d54dc1f90e29c8344c405062d5d670f18e9f Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 18 Oct 2024 12:33:40 -0400 Subject: [PATCH 11/12] merging with main again --- DesktopEdge/MainWindow.xaml.cs | 3766 ++++++++--------- DesktopEdge/Models/ZitiIdentity.cs | 348 +- .../Views/ItemRenderers/IdentityItem.xaml.cs | 774 ++-- ZitiUpdateService/UpdateService.cs | 2166 +++++----- build-test-release.ps1 | 2 +- release-streams/beta.json | 10 +- 6 files changed, 3533 insertions(+), 3533 deletions(-) diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs index 5156da7d5..da9991087 100644 --- a/DesktopEdge/MainWindow.xaml.cs +++ b/DesktopEdge/MainWindow.xaml.cs @@ -1,1883 +1,1883 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using System.Windows; -using System.Windows.Input; -using System.IO; -using System.ServiceProcess; -using System.Linq; -using System.Diagnostics; -using System.Windows.Controls; -using System.Drawing; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Media.Animation; -using System.Web; -using Microsoft.Toolkit.Uwp.Notifications; - -using ZitiDesktopEdge.Models; -using ZitiDesktopEdge.DataStructures; -using ZitiDesktopEdge.ServiceClient; -using ZitiDesktopEdge.Utility; - -using NLog; -using NLog.Config; -using NLog.Targets; -using Microsoft.Win32; - -using Ziti.Desktop.Edge.Models; - -namespace ZitiDesktopEdge { - - public partial class MainWindow : Window { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public string RECOVER = "RECOVER"; - public System.Windows.Forms.NotifyIcon notifyIcon; - public string Position = "Bottom"; - private DateTime _startDate; - private System.Windows.Forms.Timer _tunnelUptimeTimer; - private DataClient serviceClient = null; - MonitorClient monitorClient = null; - private bool _isAttached = true; - private bool _isServiceInError = false; - private int _right = 75; - private int _left = 75; - private int _top = 30; - private int defaultHeight = 540; - public int NotificationsShownCount = 0; - private double _maxHeight = 800d; - public string CurrentIcon = "white"; - private string[] suffixes = { "Bps", "kBps", "mBps", "gBps", "tBps", "pBps" }; - private string _blurbUrl = ""; - - private DateTime NextNotificationTime; - private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); - - static System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); - - public static string ThisAssemblyName; - public static string ExecutionDirectory; - public static string ExpectedLogPathRoot; - public static string ExpectedLogPathUI; - public static string ExpectedLogPathServices; - - private static ZDEWViewState state; - static MainWindow() { - asm = System.Reflection.Assembly.GetExecutingAssembly(); - ThisAssemblyName = asm.GetName().Name; - state = (ZDEWViewState)Application.Current.Properties["ZDEWViewState"]; -#if DEBUG - ExecutionDirectory = @"C:\Program Files (x86)\NetFoundry Inc\Ziti Desktop Edge"; -#else - ExecutionDirectory = Path.GetDirectoryName(asm.Location); -#endif - ExpectedLogPathRoot = Path.Combine(ExecutionDirectory, "logs"); - ExpectedLogPathUI = Path.Combine(ExpectedLogPathRoot, "UI", $"{ThisAssemblyName}.log"); - ExpectedLogPathServices = Path.Combine(ExpectedLogPathRoot, "service", $"ziti-tunneler.log"); - } - - async private void IdentityMenu_OnMessage(string message) { - await ShowBlurbAsync(message, ""); - } - - private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { - LoadIdentities(true); - } - - private List identities { - get { - return (List)Application.Current.Properties["Identities"]; - } - } - - /// - /// The MFA Toggle was toggled - /// - /// True if the toggle was on - private async void MFAToggled(bool isOn) { - if (isOn) { - ShowLoad("Generating MFA", "MFA Setup Commencing, please wait"); - - await serviceClient.EnableMFA(this.IdentityMenu.Identity.Identifier); - } else { - this.ShowMFA(IdentityMenu.Identity, 3); - } - - HideLoad(); - } - - /// - /// When a Service Client is ready to setup the MFA Authorization - /// - /// The service client - /// The MFA Event - private void ServiceClient_OnMfaEvent(object sender, MfaEvent mfa) { - HideLoad(); - this.Dispatcher.Invoke(async () => { - if (mfa.Action == "enrollment_challenge") { - string url = HttpUtility.UrlDecode(mfa.ProvisioningUrl); - string secret = HttpUtility.ParseQueryString(url)["secret"]; - this.IdentityMenu.Identity.RecoveryCodes = mfa?.RecoveryCodes?.ToArray(); - SetupMFA(this.IdentityMenu.Identity, url, secret); - } else if (mfa.Action == "auth_challenge") { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsMFANeeded = true; - identities[i].IsTimingOut = false; - break; - } - } - } else if (mfa.Action == "enrollment_verification") { - if (mfa.Successful) { - var found = identities.Find(id => id.Identifier == mfa.Identifier); - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsMFANeeded = false; - identities[i].IsMFAEnabled = true; - identities[i].IsTimingOut = false; - identities[i].LastUpdatedTime = DateTime.Now; - for (int j = 0; j < identities[i].Services.Count; j++) { - identities[i].Services[j].TimeUpdated = DateTime.Now; - identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; - } - found = identities[i]; - found.IsMFAEnabled = true; - break; - } - } - if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; - ShowMFARecoveryCodes(found); - } else { - await ShowBlurbAsync("Provided code could not be verified", ""); - } - } else if (mfa.Action == "enrollment_remove") { - if (mfa.Successful) { - var found = identities.Find(id => id.Identifier == mfa.Identifier); - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsMFAEnabled = false; - identities[i].IsMFANeeded = false; - identities[i].LastUpdatedTime = DateTime.Now; - identities[i].IsTimingOut = false; - for (int j = 0; j < identities[i].Services.Count; j++) { - identities[i].Services[j].TimeUpdated = DateTime.Now; - identities[i].Services[j].TimeoutRemaining = 0; - } - found = identities[i]; - break; - } - } - if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; - await ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", ""); - } else { - await ShowBlurbAsync("MFA Removal Failed", ""); - } - } else if (mfa.Action == "mfa_auth_status") { - var found = identities.Find(id => id.Identifier == mfa.Identifier); - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == mfa.Identifier) { - identities[i].WasNotified = false; - identities[i].WasFullNotified = false; - identities[i].IsTimingOut = false; - identities[i].IsMFANeeded = !mfa.Successful; - identities[i].LastUpdatedTime = DateTime.Now; - for (int j = 0; j < identities[i].Services.Count; j++) { - identities[i].Services[j].TimeUpdated = DateTime.Now; - identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; - } - found = identities[i]; - break; - } - } - if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; - // ShowBlurb("mfa authenticated: " + mfa.Successful, ""); - } else { - await ShowBlurbAsync("Unexpected error when processing MFA", ""); - logger.Error("unexpected action: " + mfa.Action); - } - - LoadIdentities(true); - }); - } - - /// - /// Show the MFA Setup Modal - /// - /// The Ziti Identity to Setup - public void SetupMFA(ZitiIdentity identity, string url, string secret) { - MFASetup.Opacity = 0; - MFASetup.Visibility = Visibility.Visible; - MFASetup.Margin = new Thickness(0, 0, 0, 0); - MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); - MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - MFASetup.ShowSetup(identity, url, secret); - ShowModal(); - } - - /// - /// Show the MFA Authentication Screen when it is time to authenticate - /// - /// The Ziti Identity to Authenticate - public void MFAAuthenticate(ZitiIdentity identity) { - this.ShowMFA(identity, 1); - } - - /// - /// Show MFA for the identity and set the type of screen to show - /// - /// The Identity that is currently active - /// The type of screen to show - 1 Setup, 2 Authenticate, 3 Remove MFA, 4 Regenerate Codes - private void ShowMFA(ZitiIdentity identity, int type) { - MFASetup.Opacity = 0; - MFASetup.Visibility = Visibility.Visible; - MFASetup.Margin = new Thickness(0, 0, 0, 0); - - DoubleAnimation animatin = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); - animatin.Completed += Animatin_Completed; - MFASetup.BeginAnimation(Grid.OpacityProperty, animatin); - MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - - MFASetup.ShowMFA(identity, type); - - ShowModal(); - } - - private void Animatin_Completed(object sender, EventArgs e) { - MFASetup.AuthCode.Focusable = true; - MFASetup.AuthCode.Focus(); - } - - /// - /// Show the MFA Recovery Codes - /// - /// The Ziti Identity to Authenticate - async public void ShowMFARecoveryCodes(ZitiIdentity identity) { - if (identity.IsMFAEnabled) { - if (identity.IsMFAEnabled && identity.RecoveryCodes != null) { - MFASetup.Opacity = 0; - MFASetup.Visibility = Visibility.Visible; - MFASetup.Margin = new Thickness(0, 0, 0, 0); - MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); - MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); - - MFASetup.ShowRecovery(identity.RecoveryCodes, identity); - - ShowModal(); - } else { - this.ShowMFA(IdentityMenu.Identity, 2); - } - } else { - await ShowBlurbAsync("MFA is not setup on this Identity", ""); - } - } - - /// - /// Show the modal, aniimating opacity - /// - private void ShowModal() { - ModalBg.Visibility = Visibility.Visible; - ModalBg.Opacity = 0; - DoubleAnimation animation = new DoubleAnimation(.8, TimeSpan.FromSeconds(.3)); - ModalBg.BeginAnimation(Grid.OpacityProperty, animation); - } - - /// - /// Close the various MFA windows - /// - /// The close button - /// The event arguments - private void CloseComplete(object sender, EventArgs e) { - MFASetup.Visibility = Visibility.Collapsed; - } - - /// - /// Hide the modal animating the opacity - /// - private void HideModal() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - animation.Completed += ModalHideComplete; - ModalBg.BeginAnimation(Grid.OpacityProperty, animation); - } - - /// - /// When the animation completes, set the visibility to avoid UI object conflicts - /// - /// The animation - /// The event - private void ModalHideComplete(object sender, EventArgs e) { - ModalBg.Visibility = Visibility.Collapsed; - } - - /// - /// Close the MFA Screen with animation - /// - private void DoClose(bool isComplete) { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); - animation.Completed += CloseComplete; - MFASetup.BeginAnimation(Grid.OpacityProperty, animation); - MFASetup.BeginAnimation(Grid.MarginProperty, animateThick); - HideModal(); - if (isComplete) { - if (MFASetup.Type == 1) { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == MFASetup.Identity.Identifier) { - identities[i] = MFASetup.Identity; - identities[i].LastUpdatedTime = DateTime.Now; - } - } - } - } - if (IdentityMenu.IsVisible) { - if (isComplete) { - if (MFASetup.Type == 2) { - ShowRecovery(IdentityMenu.Identity); - } else if (MFASetup.Type == 3) { - } else if (MFASetup.Type == 4) { - ShowRecovery(IdentityMenu.Identity); - } - } - IdentityMenu.UpdateView(); - } - LoadIdentities(true); - } - - private void AddIdentity(ZitiIdentity id) { - semaphoreSlim.Wait(); - if (!identities.Any(i => id.Identifier == i.Identifier)) { - identities.Add(id); - } - semaphoreSlim.Release(); - } - - private System.Windows.Forms.ContextMenu contextMenu; - private System.Windows.Forms.MenuItem contextMenuItem; - private System.ComponentModel.IContainer components; - public MainWindow() { - InitializeComponent(); - NextNotificationTime = DateTime.Now; - SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; - string nlogFile = Path.Combine(ExecutionDirectory, ThisAssemblyName + "-log.config"); - - - ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompat_OnActivated; - - bool byFile = false; - if (File.Exists(nlogFile)) { - LogManager.Configuration = new XmlLoggingConfiguration(nlogFile); - byFile = true; - } else { - var config = new LoggingConfiguration(); - // Targets where to log to: File and Console - var logfile = new FileTarget("logfile") { - FileName = ExpectedLogPathUI, - ArchiveEvery = FileArchivePeriod.Day, - ArchiveNumbering = ArchiveNumberingMode.Rolling, - MaxArchiveFiles = 7, - AutoFlush = true, - Layout = "[${date:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", - }; - var logconsole = new ConsoleTarget("logconsole"); - - // Rules for mapping loggers to targets - config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); - config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); - - // Apply config - LogManager.Configuration = config; - } - logger.Info("============================== UI started =============================="); - logger.Info("logger initialized"); - logger.Info(" - version : {0}", asm.GetName().Version.ToString()); - logger.Info(" - using file: {0}", byFile); - logger.Info(" - file: {0}", nlogFile); - logger.Info("========================================================================"); - - App.Current.MainWindow.WindowState = WindowState.Normal; - App.Current.MainWindow.Deactivated += MainWindow_Deactivated; - App.Current.MainWindow.Activated += MainWindow_Activated; - App.Current.Exit += Current_Exit; - App.Current.SessionEnding += Current_SessionEnding; - - - this.components = new System.ComponentModel.Container(); - this.contextMenu = new System.Windows.Forms.ContextMenu(); - this.contextMenuItem = new System.Windows.Forms.MenuItem(); - this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.contextMenuItem }); - - this.contextMenuItem.Index = 0; - this.contextMenuItem.Text = "&Close UI"; - this.contextMenuItem.Click += new System.EventHandler(this.contextMenuItem_Click); - - - notifyIcon = new System.Windows.Forms.NotifyIcon(); - notifyIcon.Visible = true; - notifyIcon.Click += TargetNotifyIcon_Click; - notifyIcon.Visible = true; - notifyIcon.BalloonTipClosed += NotifyIcon_BalloonTipClosed; - notifyIcon.MouseClick += NotifyIcon_MouseClick; - notifyIcon.ContextMenu = this.contextMenu; - - IdentityMenu.OnDetach += OnDetach; - MainMenu.OnDetach += OnDetach; - - this.MainMenu.MainWindow = this; - this.IdentityMenu.MainWindow = this; - SetNotifyIcon("white"); - - this.PreviewKeyDown += KeyPressed; - MFASetup.OnLoad += MFASetup_OnLoad; - MFASetup.OnError += MFASetup_OnError; - IdentityMenu.OnMessage += IdentityMenu_OnMessage; - } - - async private void MFASetup_OnError(string message) { - await ShowBlurbAsync(message, "", "error"); - } - - private static ToastButton feedbackToastButton = new ToastButton() - .SetContent("Click here to collect logs") - .AddArgument("action", "feedback"); - - private void ToastNotificationManagerCompat_OnActivated(ToastNotificationActivatedEventArgsCompat e) { - this.Dispatcher.Invoke(() => { - if (e.Argument != null && e.Argument.Length > 0) { - string[] items = e.Argument.Split(';'); - if (items.Length > 0) { - string[] values = items[0].Split('='); - if (values.Length == 2) { - string identifier = values[1]; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == identifier) { - ShowMFA(identities[i], 1); - break; - } - } - } - } - } - - ToastArguments args = ToastArguments.Parse(e.Argument); - string value = null; - if (args.TryGetValue("action", out value)) { - this.Dispatcher.Invoke(() => { - MainMenu.CollectFeedbackLogs(e, null); - }); - } - this.Show(); - this.Activate(); - }); - } - - private void KeyPressed(object sender, KeyEventArgs e) { - if (e.Key == Key.Escape) { - if (IdentityMenu.Visibility == Visibility.Visible) IdentityMenu.Visibility = Visibility.Collapsed; - else if (MainMenu.Visibility == Visibility.Visible) MainMenu.Visibility = Visibility.Collapsed; - } - } - - private void MFASetup_OnLoad(bool isComplete, string title, string message) { - if (isComplete) HideLoad(); - else ShowLoad(title, message); - } - - private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e) { - if (notifyIcon != null) { - notifyIcon.Visible = false; - notifyIcon.Icon.Dispose(); - notifyIcon.Dispose(); - notifyIcon = null; - } - Application.Current.Shutdown(); - } - - private void Current_Exit(object sender, ExitEventArgs e) { - if (notifyIcon != null) { - notifyIcon.Visible = false; - if (notifyIcon.Icon != null) { - notifyIcon.Icon.Dispose(); - } - notifyIcon.Dispose(); - notifyIcon = null; - } - } - - private void contextMenuItem_Click(object Sender, EventArgs e) { - Application.Current.Shutdown(); - } - - private void NotifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { - if (e.Button == System.Windows.Forms.MouseButtons.Left) { - System.Windows.Forms.MouseEventArgs mea = (System.Windows.Forms.MouseEventArgs)e; - this.Show(); - this.Activate(); - //Do the awesome left clickness - } else if (e.Button == System.Windows.Forms.MouseButtons.Right) { - //Do the wickedy right clickness - } else { - //Some other button from the enum :) - } - } - - private void NotifyIcon_BalloonTipClosed(object sender, EventArgs e) { - var thisIcon = (System.Windows.Forms.NotifyIcon)sender; - thisIcon.Visible = false; - thisIcon.Dispose(); - } - - private void Window_MouseDown(object sender, MouseButtonEventArgs e) { - OnDetach(e); - } - - private void OnDetach(MouseButtonEventArgs e) { - if (e.ChangedButton == MouseButton.Left) { - _isAttached = false; - IdentityMenu.Arrow.Visibility = Visibility.Collapsed; - Arrow.Visibility = Visibility.Collapsed; - MainMenu.Detach(); - this.DragMove(); - } - } - - private void MainWindow_Activated(object sender, EventArgs e) { - Placement(); - this.Show(); - this.Visibility = Visibility.Visible; - this.Opacity = 1; - } - - private void MainWindow_Deactivated(object sender, EventArgs e) { - if (this._isAttached) { - this.Visibility = Visibility.Hidden; - } - } - - private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { - if (notifyIcon != null) { - notifyIcon.Visible = false; - notifyIcon.Icon.Dispose(); - notifyIcon.Dispose(); - notifyIcon = null; - } - Application.Current.Shutdown(); - } - - private void SetCantDisplay(string title, string detailMessage, Visibility closeButtonVisibility) { - this.Dispatcher.Invoke(() => { - NoServiceView.Visibility = Visibility.Visible; - CloseErrorButton.IsEnabled = true; - CloseErrorButton.Visibility = closeButtonVisibility; - ErrorMsg.Content = title; - ErrorMsgDetail.Content = detailMessage; - SetNotifyIcon("red"); - _isServiceInError = true; - UpdateServiceView(); - }); - } - - private void TargetNotifyIcon_Click(object sender, EventArgs e) { - this.Show(); - this.Activate(); - Application.Current.MainWindow.Activate(); - } - - private void UpdateServiceView() { - if (_isServiceInError) { - AddIdAreaButton.Opacity = 0.1; - AddIdAreaButton.IsEnabled = false; - AddIdButton.Opacity = 0.1; - AddIdButton.IsEnabled = false; - ConnectButton.Opacity = 0.1; - StatArea.Opacity = 0.1; - } else { - AddIdAreaButton.Opacity = 1.0; - AddIdAreaButton.IsEnabled = true; - AddIdButton.Opacity = 1.0; - AddIdButton.IsEnabled = true; - StatArea.Opacity = 1.0; - ConnectButton.Opacity = 1.0; - } - TunnelConnected(!_isServiceInError); - } - - private void App_ReceiveString(string obj) { - Console.WriteLine(obj); - this.Show(); - this.Activate(); - } - - async private void MainWindow_Loaded(object sender, RoutedEventArgs e) { - - Window window = Window.GetWindow(App.Current.MainWindow); - ZitiDesktopEdge.App app = (ZitiDesktopEdge.App)App.Current; - app.ReceiveString += App_ReceiveString; - - // add a new service client - serviceClient = new DataClient("ui"); - serviceClient.OnClientConnected += ServiceClient_OnClientConnected; - serviceClient.OnClientDisconnected += ServiceClient_OnClientDisconnected; - serviceClient.OnIdentityEvent += ServiceClient_OnIdentityEvent; - serviceClient.OnMetricsEvent += ServiceClient_OnMetricsEvent; - serviceClient.OnServiceEvent += ServiceClient_OnServiceEvent; - serviceClient.OnTunnelStatusEvent += ServiceClient_OnTunnelStatusEvent; - serviceClient.OnMfaEvent += ServiceClient_OnMfaEvent; - serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; - serviceClient.OnBulkServiceEvent += ServiceClient_OnBulkServiceEvent; - serviceClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; - serviceClient.OnControllerEvent += ServiceClient_OnControllerEvent; - Application.Current.Properties.Add("ServiceClient", serviceClient); - - monitorClient = new MonitorClient("ui"); - monitorClient.OnClientConnected += MonitorClient_OnClientConnected; - monitorClient.OnNotificationEvent += MonitorClient_OnInstallationNotificationEvent; - monitorClient.OnServiceStatusEvent += MonitorClient_OnServiceStatusEvent; - monitorClient.OnShutdownEvent += MonitorClient_OnShutdownEvent; - monitorClient.OnCommunicationError += MonitorClient_OnCommunicationError; - monitorClient.OnReconnectFailure += MonitorClient_OnReconnectFailure; - Application.Current.Properties.Add("MonitorClient", monitorClient); - - Application.Current.Properties.Add("Identities", new List()); - MainMenu.OnAttachmentChange += AttachmentChanged; - MainMenu.OnLogLevelChanged += LogLevelChanged; - MainMenu.OnShowBlurb += MainMenu_OnShowBlurb; - IdentityMenu.OnError += IdentityMenu_OnError; - - try { - await serviceClient.ConnectAsync(); - await serviceClient.WaitForConnectionAsync(); - } catch /*ignored for now (Exception ex) */ - { - ShowServiceNotStarted(); - serviceClient.Reconnect(); - } - - try { - await monitorClient.ConnectAsync(); - await monitorClient.WaitForConnectionAsync(); - } catch /*ignored for now (Exception ex) */ - { - monitorClient.Reconnect(); - } - - IdentityMenu.OnForgot += IdentityForgotten; - Placement(); - } - - private void MonitorClient_OnCommunicationError(object sender, Exception e) { - string msg = "Communication Error with monitor?"; - ShowError(msg, e.Message); - } - - private void MainMenu_OnShowBlurb(string message) { - _ = ShowBlurbAsync(message, "", "info"); - } - - private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) { - var found = identities.Find(id => id.Identifier == e.Identifier); - if (found == null) { - logger.Warn($"{e.Action} service event for {e.Identifier} but the provided identity identifier was not found!"); - return; - } else { - if (e.RemovedServices != null) { - foreach (var removed in e.RemovedServices) { - removeService(found, removed); - } - } - if (e.AddedServices != null) { - foreach (var added in e.AddedServices) { - addService(found, added); - } - } - LoadIdentities(true); - this.Dispatcher.Invoke(() => { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - } - - private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { - var displayMFARequired = false; - var displayMFATimout = false; - foreach (var notification in e.Notification) { - var found = identities.Find(id => id.Identifier == notification.Identifier); - if (found == null) { - logger.Warn($"{e.Op} event for {notification.Identifier} but the provided identity identifier was not found!"); - continue; - } else { - found.TimeoutMessage = notification.Message; - found.MaxTimeout = notification.MfaMaximumTimeout; - found.MinTimeout = notification.MfaMinimumTimeout; - - if (notification.MfaMinimumTimeout == 0) { - // display mfa token icon - displayMFARequired = true; - } else { - displayMFATimout = true; - } - - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - } - } - - // we may need to display mfa icon, based on the timer in UI, remove found.MFAInfo.ShowMFA setting in this function. - // the below function can show mfa icon even after user authenticates successfully, in race conditions - if (displayMFARequired || displayMFATimout) { - this.Dispatcher.Invoke(() => { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - LoadIdentities(true); - } - - private void ServiceClient_OnControllerEvent(object sender, ControllerEvent e) { - logger.Debug($"==== ControllerEvent : action:{e.Action} identifier:{e.Identifier}"); - // commenting this block, because when it receives the disconnected events, identities are disabled and - // it is not allowing me to click/perform any operation on the identity - // the color of the title is also too dark, and it is not clearly visible, when the identity is disconnected - /* if (e.Action == "connected") { - var found = identities.Find(i => i.Identifier == e.Identifier); - found.IsConnected = true; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } else if (e.Action == "disconnected") { - var found = identities.Find(i => i.Identifier == e.Identifier); - found.IsConnected = false; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } */ - } - - - string nextVersionStr = null; - private void MonitorClient_OnReconnectFailure(object sender, object e) { - logger.Trace("OnReconnectFailure triggered"); - if (nextVersionStr == null) { - // check for the current version - nextVersionStr = "checking for update"; - Version nextVersion = GithubAPI.GetVersion(GithubAPI.GetJson(GithubAPI.ProdUrl)); - nextVersionStr = nextVersion.ToString(); - Version currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; //fetch from ziti? - - int compare = currentVersion.CompareTo(nextVersion); - if (compare < 0) { - MainMenu.SetAppUpgradeAvailableText("Upgrade available: " + nextVersionStr); - logger.Info("upgrade is available. Published version: {} is newer than the current version: {}", nextVersion, currentVersion); - //UpgradeAvailable(); - } else if (compare > 0) { - logger.Info("the version installed: {0} is newer than the released version: {1}", currentVersion, nextVersion); - MainMenu.SetAppIsNewer("This version is newer than the latest: " + nextVersionStr); - } else { - logger.Info("Current version installed: {0} is the same as the latest released version {1}", currentVersion, nextVersion); - MainMenu.SetAppUpgradeAvailableText(""); - } - } - } - - private void MonitorClient_OnShutdownEvent(object sender, StatusEvent e) { - logger.Info("The monitor has indicated the application should shut down."); - this.Dispatcher.Invoke(() => { - Application.Current.Shutdown(); - }); - } - - private void MonitorClient_OnServiceStatusEvent(object sender, MonitorServiceStatusEvent evt) { - this.Dispatcher.Invoke(() => { - try { - if (evt.Message?.ToLower() == "upgrading") { - logger.Info("The monitor has indicated an upgrade is in progress. Shutting down the UI"); - UpgradeSentinel.StartUpgradeSentinel(); - - App.Current.Exit -= Current_Exit; - logger.Info("Removed Current_Exit handler"); - notifyIcon.Visible = false; - notifyIcon.Icon.Dispose(); - notifyIcon.Dispose(); - Application.Current.Shutdown(); - return; - } - SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); - if (evt.Code != 0) { - logger.Error("CODE: " + evt.Code); - if (MainMenu.ShowUnexpectedFailure) { - ShowToast("The data channel has stopped unexpectedly", $"If this keeps happening please collect logs and report the issue.", feedbackToastButton); - } - } - MainMenu.ShowUpdateAvailable(); - logger.Debug("MonitorClient_OnServiceStatusEvent: {0}", evt.Status); - Application.Current.Properties["ReleaseStream"] = evt.ReleaseStream; - - ServiceControllerStatus status = (ServiceControllerStatus)Enum.Parse(typeof(ServiceControllerStatus), evt.Status); - - switch (status) { - case ServiceControllerStatus.Running: - logger.Info("Service is started"); - break; - case ServiceControllerStatus.Stopped: - logger.Info("Service is stopped"); - ShowServiceNotStarted(); - break; - case ServiceControllerStatus.StopPending: - logger.Info("Service is stopping..."); - - this.Dispatcher.Invoke(async () => { - SetCantDisplay("The Service is Stopping", "Please wait while the service stops", Visibility.Visible); - await WaitForServiceToStop(DateTime.Now + TimeSpan.FromSeconds(30)); - }); - break; - case ServiceControllerStatus.StartPending: - logger.Info("Service is starting..."); - break; - case ServiceControllerStatus.PausePending: - logger.Warn("UNEXPECTED STATUS: PausePending"); - break; - case ServiceControllerStatus.Paused: - logger.Warn("UNEXPECTED STATUS: Paused"); - break; - default: - logger.Warn("UNEXPECTED STATUS: {0}", evt.Status); - break; - } - } catch (Exception ex) { - logger.Warn(ex, "unexpected exception in MonitorClient_OnServiceStatusEvent? {0}", ex.Message); - } - }); - } - - public void SetAutomaticUpdateEnabled(string enabled, string url) { - this.Dispatcher.Invoke(() => { - state.AutomaticUpdatesDisabled = bool.Parse(enabled); - state.AutomaticUpdateURL = url; - }); - } - - private void MonitorClient_OnInstallationNotificationEvent(object sender, InstallationNotificationEvent evt) { - this.Dispatcher.Invoke(() => { - logger.Debug("MonitorClient_OnInstallationNotificationEvent: {0}", evt.Message); - switch (evt.Message?.ToLower()) { - case "installationupdate": - logger.Debug("Installation Update is available - {0}", evt.ZDEVersion); - var remaining = evt.InstallTime - DateTime.Now; - - state.PendingUpdate.Version = evt.ZDEVersion; - state.PendingUpdate.InstallTime = evt.InstallTime; - state.UpdateAvailable = true; - SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); - MainMenu.ShowUpdateAvailable(); - AlertCanvas.Visibility = Visibility.Visible; - - if (isToastEnabled()) { - if (!state.AutomaticUpdatesDisabled) { - if (remaining.TotalSeconds < 60) { - //this is an immediate update - show a different message - ShowToast("Ziti Desktop Edge will initiate auto installation in the next minute!"); - } else { - if (DateTime.Now > NextNotificationTime) { - ShowToast($"Update {evt.ZDEVersion} is available for Ziti Desktop Edge and will be automatically installed by " + evt.InstallTime); - NextNotificationTime = DateTime.Now + evt.NotificationDuration; - } else { - logger.Debug("Skipping notification. Time until next notification {} seconds which is at {}", (int)((NextNotificationTime - DateTime.Now).TotalSeconds), NextNotificationTime); - } - } - } else { - ShowToast("New version available", $"Version {evt.ZDEVersion} is available for Ziti Desktop Edge", null); - } - SetNotifyIcon(""); - // display a tag in UI and a button for the update software - } - break; - case "configuration changed": - break; - default: - logger.Debug("unexpected event type?"); - break; - } - }); - } - - private bool isToastEnabled() { - bool result; - //only show notifications once if automatic updates are disabled - if (NotificationsShownCount == 0) { - result = true; //regardless - if never notified, always return true - } else { - result = !state.AutomaticUpdatesDisabled; - } - return result; - } - - private void ShowToast(string header, string message, ToastButton button) { - try { - logger.Debug("showing toast: {} {}", header, message); - var builder = new ToastContentBuilder() - .AddArgument("notbutton", "click") - .AddText(header) - .AddText(message); - if (button != null) { - builder.AddButton(button); - } - builder.Show(); - NotificationsShownCount++; - } catch { - logger.Warn("couldn't show toast: {} {}", header, message); - } - } - - - private void ShowToast(string message) { - ShowToast("Important Notice", message, null); - } - - async private Task WaitForServiceToStop(DateTime until) { - //continually poll for the service to stop. If it is stuck - ask the user if they want to try to force - //close the service - while (DateTime.Now < until) { - await Task.Delay(250); - MonitorServiceStatusEvent resp = await monitorClient.StatusAsync(); - if (resp.IsStopped()) { - // good - that's what we are waiting for... - return; - } else { - // bad - not stopped yet... - logger.Debug("Waiting for service to stop... Still not stopped yet. Status: {0}", resp.Status); - } - } - // real bad - means it's stuck probably. Ask the user if they want to try to force it... - logger.Warn("Waiting for service to stop... Service did not reach stopped state in the expected amount of time."); - SetCantDisplay("The Service Appears Stuck", "Would you like to try to force close the service?", Visibility.Visible); - CloseErrorButton.Content = "Force Quit"; - CloseErrorButton.Click -= CloseError; - CloseErrorButton.Click += ForceQuitButtonClick; - } - - async private void ForceQuitButtonClick(object sender, RoutedEventArgs e) { - MonitorServiceStatusEvent status = await monitorClient.ForceTerminateAsync(); - if (status.IsStopped()) { - //good - CloseErrorButton.Click += CloseError; //reset the close button... - CloseErrorButton.Click -= ForceQuitButtonClick; - } else { - //bad... - SetCantDisplay("The Service Is Still Running", "Current status is: " + status.Status, Visibility.Visible); - } - } - - async private void StartZitiService(object sender, RoutedEventArgs e) { - try { - ShowLoad("Starting", "Starting the data service"); - logger.Info("StartZitiService"); - var r = await monitorClient.StartServiceAsync(); - if (r.Code != 0) { - logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); - } else { - logger.Info("Service started!"); - CloseErrorButton.Click -= StartZitiService; - CloseError(null, null); - } - } catch (MonitorServiceException me) { - logger.Warn("the monitor service appears offline. {0}", me); - CloseErrorButton.IsEnabled = true; - HideLoad(); - ShowError("Error Starting Service", "The monitor service is offline"); - } catch (Exception ex) { - logger.Error(ex, "UNEXPECTED ERROR!"); - CloseErrorButton.IsEnabled = true; - HideLoad(); - ShowError("Unexpected Error", "Code 2:" + ex.Message); - } - CloseErrorButton.IsEnabled = true; - // HideLoad(); - } - - private void ShowServiceNotStarted() { - TunnelConnected(false); - LoadIdentities(true); - } - - private void MonitorClient_OnClientConnected(object sender, object e) { - logger.Debug("MonitorClient_OnClientConnected"); - MainMenu.SetAppUpgradeAvailableText(""); - } - - async private Task LogLevelChanged(string level) { - int logsSet = 0; - try { - await serviceClient.SetLogLevelAsync(level); - logsSet++; - await monitorClient.SetLogLevelAsync(level); - logsSet++; - Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(level); - return true; - } catch (Exception ex) { - logger.Error(ex, "Unexpected error. logsSet: {0}", logsSet); - if (logsSet > 1) { - await ShowBlurbAsync("Unexpected error setting logs?", ""); - } else if (logsSet > 0) { - await ShowBlurbAsync("Failed to set monitor client log level", ""); - } else { - await ShowBlurbAsync("Failed to set log levels", ""); - } - } - return false; - } - - private void IdentityMenu_OnError(string message) { - ShowError("Identity Error", message); - } - - private void ServiceClient_OnClientConnected(object sender, object e) { - this.Dispatcher.Invoke(() => { - MainMenu.Connected(); - NoServiceView.Visibility = Visibility.Collapsed; - _isServiceInError = false; - UpdateServiceView(); - SetNotifyIcon("white"); - LoadIdentities(true); - }); - } - - private void ServiceClient_OnClientDisconnected(object sender, object e) { - this.Dispatcher.Invoke(() => { - IdentityMenu.Visibility = Visibility.Collapsed; - MFASetup.Visibility = Visibility.Collapsed; - HideModal(); - MainMenu.Disconnected(); - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = (IdentityItem)IdList.Children[i]; - item.StopTimers(); - } - IdList.Children.Clear(); - if (e != null) { - logger.Debug(e.ToString()); - } - //SetCantDisplay("Start the Ziti Tunnel Service to continue"); - SetNotifyIcon("red"); - ShowServiceNotStarted(); - }); - } - - /// - /// If an identity gets added late, execute this. - /// - /// Do not update services for identity events - /// - /// The sending service - /// The identity event - private void ServiceClient_OnIdentityEvent(object sender, IdentityEvent e) { - if (e == null) return; - - ZitiIdentity zid = ZitiIdentity.FromClient(e.Id); - logger.Debug($"==== IdentityEvent : action:{e.Action} identifer:{e.Id.Identifier} name:{e.Id.Name} "); - - this.Dispatcher.Invoke(async () => { - if (e.Action == "added") { - var found = identities.Find(i => i.Identifier == e.Id.Identifier); - if (found == null) { - AddIdentity(zid); - LoadIdentities(true); - } else { - // means we likely are getting an update for some reason. compare the identities and use the latest info - if (zid.Name != null && zid.Name.Length > 0) found.Name = zid.Name; - if (zid.ControllerUrl != null && zid.ControllerUrl.Length > 0) found.ControllerUrl = zid.ControllerUrl; - if (zid.ContollerVersion != null && zid.ContollerVersion.Length > 0) found.ContollerVersion = zid.ContollerVersion; - found.IsEnabled = zid.IsEnabled; - found.IsMFAEnabled = e.Id.MfaEnabled; - found.IsConnected = true; - found.NeedsExtAuth = e.Id.NeedsExtAuth; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } - } else if (e.Action == "updated") { - //this indicates that all updates have been sent to the UI... wait for 2 seconds then trigger any ui updates needed - await Task.Delay(2000); - LoadIdentities(true); - } else if (e.Action == "connected") { - var found = identities.Find(i => i.Identifier == e.Id.Identifier); - found.IsConnected = true; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } else if (e.Action == "disconnected") { - var found = identities.Find(i => i.Identifier == e.Id.Identifier); - found.IsConnected = false; - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == found.Identifier) { - identities[i] = found; - break; - } - } - LoadIdentities(true); - } else if (e.Action == "needs_ext_login") { - logger.Debug("needs_ext_login action received"); //handled through identity event at the moment (forever?) - } else { - logger.Warn("unexpected action received: {}", e.Action); - IdentityForgotten(ZitiIdentity.FromClient(e.Id)); - } - }); - logger.Debug($"IDENTITY EVENT. Action: {e.Action} identifier: {zid.Identifier}"); - } - - private void ServiceClient_OnMetricsEvent(object sender, List ids) { - if (ids != null) { - long totalUp = 0; - long totalDown = 0; - foreach (var id in ids) { - //logger.Debug($"==== MetricsEvent : id {id.Name} down: {id.Metrics.Down} up:{id.Metrics.Up}"); - if (id?.Metrics != null) { - totalDown += id.Metrics.Down; - totalUp += id.Metrics.Up; - } - } - this.Dispatcher.Invoke(() => { - SetSpeed(totalUp, UploadSpeed, UploadSpeedLabel); - SetSpeed(totalDown, DownloadSpeed, DownloadSpeedLabel); - }); - } - } - - public void SetSpeed(decimal bytes, Label speed, Label speedLabel) { - int counter = 0; - while (Math.Round(bytes / 1024) >= 1) { - bytes = bytes / 1024; - counter++; - } - speed.Content = bytes.ToString("0.0"); - speedLabel.Content = suffixes[counter]; - } - - private void ServiceClient_OnServiceEvent(object sender, ServiceEvent e) { - if (e == null) return; - - logger.Debug($"==== ServiceEvent : action:{e.Action} identifier:{e.Identifier} name:{e.Service.Name} "); - var found = identities.Find(id => id.Identifier == e.Identifier); - if (found == null) { - logger.Debug($"{e.Action} service event for {e.Service.Name} but the provided identity identifier {e.Identifier} is not found!"); - return; - } - - if (e.Action == "added") { - addService(found, e.Service); - } else { - removeService(found, e.Service); - } - LoadIdentities(true); - this.Dispatcher.Invoke(() => { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - - private void addService(ZitiIdentity found, Service added) { - ZitiService zs = new ZitiService(added); - var svc = found.Services.Find(s => s.Name == zs.Name); - if (svc == null) { - logger.Debug("Service Added: " + zs.Name); - found.Services.Add(zs); - if (zs.HasFailingPostureCheck()) { - found.HasServiceFailingPostureCheck = true; - if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) { - found.IsMFANeeded = true; - } - } - } else { - logger.Debug("the service named " + zs.Name + " is already accounted for on this identity."); - } - } - - private void removeService(ZitiIdentity found, Service removed) { - logger.Debug("removing the service named: {0}", removed.Name); - found.Services.RemoveAll(s => s.Name == removed.Name); - } - - private void ServiceClient_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { - if (e == null) return; //just skip it for now... - logger.Debug($"==== TunnelStatusEvent: "); - Application.Current.Properties.Remove("CurrentTunnelStatus"); - Application.Current.Properties.Add("CurrentTunnelStatus", e.Status); - e.Status.Dump(Console.Out); - this.Dispatcher.Invoke(() => { - /*if (e.ApiVersion != DataClient.EXPECTED_API_VERSION) { - SetCantDisplay("Version mismatch!", "The version of the Service is not compatible", Visibility.Visible); - return; - }*/ - this.MainMenu.LogLevel = e.Status.LogLevel; - Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.Status.LogLevel); - InitializeTimer((int)e.Status.Duration); - LoadStatusFromService(e.Status); - LoadIdentities(true); - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - if (deets.IsVisible) { - deets.UpdateView(); - } - }); - } - - private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { - if (e.LogLevel != null) { - SetLogLevel_monitor(e.LogLevel); - this.Dispatcher.Invoke(() => { - this.MainMenu.LogLevel = e.LogLevel; - Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.LogLevel); - }); - } - } - - async private void SetLogLevel_monitor(string loglevel) { - await monitorClient.SetLogLevelAsync(loglevel); - } - - private void IdentityForgotten(ZitiIdentity forgotten) { - ZitiIdentity idToRemove = null; - foreach (var id in identities) { - if (id.Identifier == forgotten.Identifier) { - idToRemove = id; - break; - } - } - identities.Remove(idToRemove); - LoadIdentities(false); - } - - private void AttachmentChanged(bool attached) { - _isAttached = attached; - if (!_isAttached) { - SetLocation(); - } - Placement(); - MainMenu.Visibility = Visibility.Collapsed; - } - - private void LoadStatusFromService(TunnelStatus status) { - //clear any identities - this.identities.Clear(); - - if (status != null) { - _isServiceInError = false; - UpdateServiceView(); - NoServiceView.Visibility = Visibility.Collapsed; - SetNotifyIcon("green"); - if (!Application.Current.Properties.Contains("ip")) { - Application.Current.Properties.Add("ip", status?.IpInfo?.Ip); - } else { - Application.Current.Properties["ip"] = status?.IpInfo?.Ip; - } - if (!Application.Current.Properties.Contains("subnet")) { - Application.Current.Properties.Add("subnet", status?.IpInfo?.Subnet); - } else { - Application.Current.Properties["subnet"] = status?.IpInfo?.Subnet; - } - if (!Application.Current.Properties.Contains("mtu")) { - Application.Current.Properties.Add("mtu", status?.IpInfo?.MTU); - } else { - Application.Current.Properties["mtu"] = status?.IpInfo?.MTU; - } - if (!Application.Current.Properties.Contains("dns")) { - Application.Current.Properties.Add("dns", status?.IpInfo?.DNS); - } else { - Application.Current.Properties["dns"] = status?.IpInfo?.DNS; - } - if (!Application.Current.Properties.Contains("dnsenabled")) { - Application.Current.Properties.Add("dnsenabled", status?.AddDns); - } else { - Application.Current.Properties["dnsenabled"] = status?.AddDns; - } - - string key = "ApiPageSize"; - if (!Application.Current.Properties.Contains(key)) { - Application.Current.Properties.Add(key, status?.ApiPageSize); - } else { - Application.Current.Properties[key] = status?.ApiPageSize; - } - - foreach (var id in status.Identities) { - updateViewWithIdentity(id); - } - LoadIdentities(true); - } else { - ShowServiceNotStarted(); - } - } - - private void updateViewWithIdentity(Identity id) { - var zid = ZitiIdentity.FromClient(id); - foreach (var i in identities) { - if (i.Identifier == zid.Identifier) { - identities.Remove(i); - break; - } - } - identities.Add(zid); - } - - private bool IsTimingOut() { - if (identities != null) { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].IsTimingOut) return true; - } - } - return false; - } - - private bool IsTimedOut() { - if (identities != null) { - return identities.Any(i => i.IsTimedOut); - } - return false; - } - - private void SetNotifyIcon(string iconPrefix) { - if (iconPrefix != "") CurrentIcon = iconPrefix; - string icon = "pack://application:,,/Assets/Images/ziti-" + CurrentIcon; - if (state.UpdateAvailable) { - icon += "-update"; - } else { - if (IsTimedOut()) { - icon += "-mfa"; - } else { - if (IsTimingOut()) { - icon += "-timer"; - } - } - } - icon += ".ico"; - var iconUri = new Uri(icon); - Stream iconStream = Application.GetResourceStream(iconUri).Stream; - notifyIcon.Icon = new Icon(iconStream); - - Application.Current.MainWindow.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconUri); - } - - private void LoadIdentities(Boolean repaint) { - this.Dispatcher.Invoke(() => { - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = (IdentityItem)IdList.Children[i]; - item.StopTimers(); - } - IdList.Children.Clear(); - IdList.Height = 0; - var desktopWorkingArea = SystemParameters.WorkArea; - if (_maxHeight > (desktopWorkingArea.Height - 10)) _maxHeight = desktopWorkingArea.Height - 10; - if (_maxHeight < 100) _maxHeight = 100; - IdList.MaxHeight = _maxHeight - 520; - ZitiIdentity[] ids = identities.OrderBy(i => (i.Name != null) ? i.Name.ToLower() : i.Name).ToArray(); - MainMenu.SetupIdList(ids); - if (ids.Length > 0 && serviceClient.Connected) { - double height = defaultHeight + (ids.Length * 60); - if (height > _maxHeight) height = _maxHeight; - this.Height = height; - IdentityMenu.SetHeight(this.Height - 160); - MainMenu.IdentitiesButton.Visibility = Visibility.Visible; - foreach (var id in ids) { - IdentityItem idItem = new IdentityItem(); - - idItem.ToggleStatus.IsEnabled = id.IsEnabled; - if (id.IsEnabled) idItem.ToggleStatus.Content = "ENABLED"; - else idItem.ToggleStatus.Content = "DISABLED"; - - idItem.Authenticate += IdItem_Authenticate; - idItem.OnStatusChanged += Id_OnStatusChanged; - idItem.Identity = id; - idItem.IdentityChanged += IdItem_IdentityChanged; - - if (repaint) { - idItem.RefreshUI(); - } - - IdList.Children.Add(idItem); - - if (IdentityMenu.Visibility == Visibility.Visible) { - if (id.Identifier == IdentityMenu.Identity.Identifier) IdentityMenu.Identity = id; - } - } - DoubleAnimation animation = new DoubleAnimation((double)(ids.Length * 64), TimeSpan.FromSeconds(.2)); - IdList.BeginAnimation(FrameworkElement.HeightProperty, animation); - IdListScroller.Visibility = Visibility.Visible; - } else { - this.Height = defaultHeight; - MainMenu.IdentitiesButton.Visibility = Visibility.Collapsed; - IdListScroller.Visibility = Visibility.Visible; - - } - AddIdButton.Visibility = Visibility.Visible; - AddIdAreaButton.Visibility = Visibility.Visible; - - Placement(); - SetNotifyIcon(""); - }); - } - - private void IdItem_IdentityChanged(ZitiIdentity identity) { - for (int i = 0; i < identities.Count; i++) { - if (identities[i].Identifier == identity.Identifier) { - identities[i] = identity; - break; - } - } - SetNotifyIcon(""); - } - - private void IdItem_Authenticate(ZitiIdentity identity) { - ShowAuthenticate(identity); - } - - private void Id_OnStatusChanged(bool attached) { - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = IdList.Children[i] as IdentityItem; - if (item.ToggleSwitch.Enabled) break; - } - } - - private void TunnelConnected(bool isConnected) { - this.Dispatcher.Invoke(() => { - if (isConnected) { - ConnectButton.Visibility = Visibility.Collapsed; - DisconnectButton.Visibility = Visibility.Visible; - MainMenu.Connected(); - HideLoad(); - SetNotifyIcon("green"); - } else { - ConnectButton.Visibility = Visibility.Visible; - DisconnectButton.Visibility = Visibility.Collapsed; - IdentityMenu.Visibility = Visibility.Collapsed; - MainMenu.Visibility = Visibility.Collapsed; - HideBlurb(); - MainMenu.Disconnected(); - DownloadSpeed.Content = "0.0"; - UploadSpeed.Content = "0.0"; - } - }); - } - - private void SetLocation() { - var desktopWorkingArea = SystemParameters.WorkArea; - - var height = MainView.ActualHeight; - IdentityMenu.MainHeight = MainView.ActualHeight; - MainMenu.MainHeight = MainView.ActualHeight; - - Rectangle trayRectangle = WinAPI.GetTrayRectangle(); - if (trayRectangle.Top < 20) { - this.Position = "Top"; - this.Top = desktopWorkingArea.Top + _top; - this.Left = desktopWorkingArea.Right - this.Width - _right; - Arrow.SetValue(Canvas.TopProperty, (double)0); - Arrow.SetValue(Canvas.LeftProperty, (double)185); - MainMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - } else if (trayRectangle.Left < 20) { - this.Position = "Left"; - this.Left = _left; - this.Top = desktopWorkingArea.Bottom - this.ActualHeight - 75; - Arrow.SetValue(Canvas.TopProperty, height - 200); - Arrow.SetValue(Canvas.LeftProperty, (double)0); - MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); - } else if (desktopWorkingArea.Right == (double)trayRectangle.Left) { - this.Position = "Right"; - this.Left = desktopWorkingArea.Right - this.Width - 20; - this.Top = desktopWorkingArea.Bottom - height - 75; - Arrow.SetValue(Canvas.TopProperty, height - 100); - Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); - MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); - } else { - this.Position = "Bottom"; - this.Left = desktopWorkingArea.Right - this.Width - 75; - this.Top = desktopWorkingArea.Bottom - height; - Arrow.SetValue(Canvas.TopProperty, height - 35); - Arrow.SetValue(Canvas.LeftProperty, (double)185); - MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); - MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); - IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); - } - } - public void Placement() { - if (_isAttached) { - Arrow.Visibility = Visibility.Visible; - IdentityMenu.Arrow.Visibility = Visibility.Visible; - SetLocation(); - } else { - IdentityMenu.Arrow.Visibility = Visibility.Collapsed; - Arrow.Visibility = Visibility.Collapsed; - } - } - - private void OpenIdentity(ZitiIdentity identity) { - IdentityMenu.Identity = identity; - } - - private void ShowMenu(object sender, MouseButtonEventArgs e) { - MainMenu.Visibility = Visibility.Visible; - } - - async private void AddIdentity(object sender, MouseButtonEventArgs e) { - UIModel.HideOnLostFocus = false; - Microsoft.Win32.OpenFileDialog jwtDialog = new Microsoft.Win32.OpenFileDialog(); - UIModel.HideOnLostFocus = true; - jwtDialog.DefaultExt = ".jwt"; - jwtDialog.Filter = "Ziti Identities (*.jwt)|*.jwt"; - - if (jwtDialog.ShowDialog() == true) { - ShowLoad("Adding Identity", "Please wait while the identity is added"); - string fileContent = File.ReadAllText(jwtDialog.FileName); - - try { - Identity createdId = await serviceClient.AddIdentityAsync(System.IO.Path.GetFileName(jwtDialog.FileName), false, fileContent); - - if (createdId != null) { - var zid = ZitiIdentity.FromClient(createdId); - AddIdentity(zid); - LoadIdentities(true); - await serviceClient.IdentityOnOffAsync(createdId.Identifier, true); - }/* else { - ShowError("Identity Error", "Identity Id was null, please try again"); - }*/ - } catch (ServiceException se) { - ShowError(se.Message, se.AdditionalInfo); - } catch (Exception ex) { - ShowError("Unexpected Error", "Code 2:" + ex.Message); - } - HideLoad(); - } - } - - private void OnTimedEvent(object sender, EventArgs e) { - TimeSpan span = (DateTime.Now - _startDate); - int hours = span.Hours; - int minutes = span.Minutes; - int seconds = span.Seconds; - var hoursString = (hours > 9) ? hours.ToString() : "0" + hours; - var minutesString = (minutes > 9) ? minutes.ToString() : "0" + minutes; - var secondsString = (seconds > 9) ? seconds.ToString() : "0" + seconds; - ConnectedTime.Content = hoursString + ":" + minutesString + ":" + secondsString; - } - - private void InitializeTimer(int millisAgoStarted) { - _startDate = DateTime.Now.Subtract(new TimeSpan(0, 0, 0, 0, millisAgoStarted)); - _tunnelUptimeTimer = new System.Windows.Forms.Timer(); - _tunnelUptimeTimer.Interval = 100; - _tunnelUptimeTimer.Tick += OnTimedEvent; - _tunnelUptimeTimer.Enabled = true; - _tunnelUptimeTimer.Start(); - } - - async private Task DoConnectAsync() { - try { - SetNotifyIcon("green"); - TunnelConnected(true); - - for (int i = 0; i < identities.Count; i++) { - await serviceClient.IdentityOnOffAsync(identities[i].Identifier, true); - } - for (int i = 0; i < IdList.Children.Count; i++) { - IdentityItem item = IdList.Children[i] as IdentityItem; - item._identity.IsEnabled = true; - item.RefreshUI(); - } - } catch (ServiceException se) { - ShowError("Error Occurred", se.Message + " " + se.AdditionalInfo); - } catch (Exception ex) { - ShowError("Unexpected Error", "Code 3:" + ex.Message); - } - } - - async private void Disconnect(object sender, RoutedEventArgs e) { - try { - ShowLoad("Disabling Service", "Please wait for the service to stop."); - var r = await monitorClient.StopServiceAsync(); - if (r.Code != 0) { - logger.Warn("ERROR: Error:{0}, Message:{1}", r.Error, r.Message); - } else { - logger.Info("Service stopped!"); - SetNotifyIcon("white"); - } - } catch (MonitorServiceException me) { - logger.Warn("the monitor service appears offline. {0}", me); - ShowError("Error Disabling Service", "The monitor service is offline"); - } catch (Exception ex) { - logger.Error(ex, "unexpected error: {0}", ex.Message); - ShowError("Error Disabling Service", "An error occurred while trying to disable the data service. Is the monitor service running?"); - } - HideLoad(); - } - - internal void ShowLoad(string title, string msg) { - this.Dispatcher.Invoke(() => { - LoadingDetails.Text = msg; - LoadingTitle.Content = title; - LoadProgress.IsIndeterminate = true; - LoadingScreen.Visibility = Visibility.Visible; - UpdateLayout(); - }); - } - - internal void HideLoad() { - this.Dispatcher.Invoke(() => { - LoadingScreen.Visibility = Visibility.Collapsed; - LoadProgress.IsIndeterminate = false; - }); - } - - private void FormFadeOut_Completed(object sender, EventArgs e) { - closeCompleted = true; - } - private bool closeCompleted = false; - private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { - if (!closeCompleted) { - FormFadeOut.Begin(); - e.Cancel = true; - } - } - - public void ShowError(string title, string message) { - this.Dispatcher.Invoke(() => { - ErrorTitle.Content = title; - ErrorDetails.Text = message; - ErrorView.Visibility = Visibility.Visible; - }); - } - - private void CloseError(object sender, RoutedEventArgs e) { - this.Dispatcher.Invoke(() => { - ErrorView.Visibility = Visibility.Collapsed; - NoServiceView.Visibility = Visibility.Collapsed; - CloseErrorButton.IsEnabled = true; - }); - } - - private void CloseApp(object sender, RoutedEventArgs e) { - Application.Current.Shutdown(); - } - - private void MainUI_Deactivated(object sender, EventArgs e) { - if (this._isAttached) { -#if DEBUG - logger.Debug("debug is enabled - windows pinned"); -#else - this.Visibility = Visibility.Collapsed; -#endif - } - } - - private void Label_MouseDoubleClick(object sender, MouseButtonEventArgs e) { - Placement(); - } - - int cur = 0; - LogLevelEnum[] levels = new LogLevelEnum[] { LogLevelEnum.FATAL, LogLevelEnum.ERROR, LogLevelEnum.WARN, LogLevelEnum.INFO, LogLevelEnum.DEBUG, LogLevelEnum.TRACE, LogLevelEnum.VERBOSE }; - public LogLevelEnum NextLevel() { - cur++; - if (cur > 6) { - cur = 0; - } - return levels[cur]; - } - - private void IdList_LayoutUpdated(object sender, EventArgs e) { - Placement(); - } - - async private void CollectLogFileClick(object sender, RoutedEventArgs e) { - await CollectLogFiles(); - } - async private Task CollectLogFiles() { - MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); - if (resp != null) { - logger.Info("response: {0}", resp.Message); - } else { - ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback. Is the monitor service running?"); - } - } - - /// - /// Show the blurb as a growler notification - /// - /// The message to show - /// The url or action name to execute - public async Task ShowBlurbAsync(string message, string url, string level = "error") { - RedBlurb.Visibility = Visibility.Collapsed; - InfoBlurb.Visibility = Visibility.Collapsed; - if (level == "error") { - RedBlurb.Visibility = Visibility.Visible; - } else { - InfoBlurb.Visibility = Visibility.Visible; - } - Blurb.Content = message; - _blurbUrl = url; - BlurbArea.Visibility = Visibility.Visible; - BlurbArea.Opacity = 0; - BlurbArea.Margin = new Thickness(0, 0, 0, 0); - DoubleAnimation animation = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(15, 0, 15, 15), TimeSpan.FromSeconds(.3)); - BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); - BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); - await Task.Delay(5000); - HideBlurb(); - } - - /// - /// Execute the hide operation wihout an action from the growler - /// - /// The object that was clicked - /// The click event - private void DoHideBlurb(object sender, MouseButtonEventArgs e) { - HideBlurb(); - } - - /// - /// Hide the blurb area - /// - private void HideBlurb() { - DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); - ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); - animation.Completed += HideComplete; - BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); - BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); - } - - /// - /// Hide the blurb area after the animation fades out - /// - /// The animation object - /// The completion event - private void HideComplete(object sender, EventArgs e) { - BlurbArea.Visibility = Visibility.Collapsed; - } - - /// - /// Execute a predefined action or url when the pop up is clicked - /// - /// The object that was clicked - /// The click event - private void BlurbAction(object sender, MouseButtonEventArgs e) { - if (_blurbUrl.Length > 0) { - // So this simply execute a url but you could do like if (_blurbUrl=="DoSomethingNifty") CallNifyFunction(); - if (_blurbUrl == this.RECOVER) { - this.ShowMFA(IdentityMenu.Identity, 4); - } else { - Process.Start(new ProcessStartInfo(_blurbUrl) { UseShellExecute = true }); - } - HideBlurb(); - } else { - HideBlurb(); - } - } - - private void ShowAuthenticate(ZitiIdentity identity) { - MFAAuthenticate(identity); - } - - private void ShowRecovery(ZitiIdentity identity) { - ShowMFARecoveryCodes(identity); - } - - - - - - private ICommand someCommand; - public ICommand SomeCommand { - get { - return someCommand - ?? (someCommand = new ActionCommand(() => { - if (DebugForm.Visibility == Visibility.Hidden) { - DebugForm.Visibility = Visibility.Visible; - } else { - DebugForm.Visibility = Visibility.Hidden; - } - })); - } - set { - someCommand = value; - } - } - - private void DoLoading(bool isComplete) { - if (isComplete) HideLoad(); - else ShowLoad("Loading", "Please Wait."); - } - - private void Label_MouseDoubleClick_1(object sender, MouseButtonEventArgs e) { - ShowToast("here's a toast all rightddd..."); - } - } - - public class ActionCommand : ICommand { - private readonly Action _action; - - public ActionCommand(Action action) { - _action = action; - } - - public void Execute(object parameter) { - _action(); - } - - public bool CanExecute(object parameter) { - return true; - } -#pragma warning disable CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used - public event EventHandler CanExecuteChanged; -#pragma warning restore CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used - } -} +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Input; +using System.IO; +using System.ServiceProcess; +using System.Linq; +using System.Diagnostics; +using System.Windows.Controls; +using System.Drawing; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Media.Animation; +using System.Web; +using Microsoft.Toolkit.Uwp.Notifications; + +using ZitiDesktopEdge.Models; +using ZitiDesktopEdge.DataStructures; +using ZitiDesktopEdge.ServiceClient; +using ZitiDesktopEdge.Utility; + +using NLog; +using NLog.Config; +using NLog.Targets; +using Microsoft.Win32; + +using Ziti.Desktop.Edge.Models; + +namespace ZitiDesktopEdge { + + public partial class MainWindow : Window { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public string RECOVER = "RECOVER"; + public System.Windows.Forms.NotifyIcon notifyIcon; + public string Position = "Bottom"; + private DateTime _startDate; + private System.Windows.Forms.Timer _tunnelUptimeTimer; + private DataClient serviceClient = null; + MonitorClient monitorClient = null; + private bool _isAttached = true; + private bool _isServiceInError = false; + private int _right = 75; + private int _left = 75; + private int _top = 30; + private int defaultHeight = 540; + public int NotificationsShownCount = 0; + private double _maxHeight = 800d; + public string CurrentIcon = "white"; + private string[] suffixes = { "Bps", "kBps", "mBps", "gBps", "tBps", "pBps" }; + private string _blurbUrl = ""; + + private DateTime NextNotificationTime; + private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); + + static System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); + + public static string ThisAssemblyName; + public static string ExecutionDirectory; + public static string ExpectedLogPathRoot; + public static string ExpectedLogPathUI; + public static string ExpectedLogPathServices; + + private static ZDEWViewState state; + static MainWindow() { + asm = System.Reflection.Assembly.GetExecutingAssembly(); + ThisAssemblyName = asm.GetName().Name; + state = (ZDEWViewState)Application.Current.Properties["ZDEWViewState"]; +#if DEBUG + ExecutionDirectory = @"C:\Program Files (x86)\NetFoundry Inc\Ziti Desktop Edge"; +#else + ExecutionDirectory = Path.GetDirectoryName(asm.Location); +#endif + ExpectedLogPathRoot = Path.Combine(ExecutionDirectory, "logs"); + ExpectedLogPathUI = Path.Combine(ExpectedLogPathRoot, "UI", $"{ThisAssemblyName}.log"); + ExpectedLogPathServices = Path.Combine(ExpectedLogPathRoot, "service", $"ziti-tunneler.log"); + } + + async private void IdentityMenu_OnMessage(string message) { + await ShowBlurbAsync(message, ""); + } + + private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { + LoadIdentities(true); + } + + private List identities { + get { + return (List)Application.Current.Properties["Identities"]; + } + } + + /// + /// The MFA Toggle was toggled + /// + /// True if the toggle was on + private async void MFAToggled(bool isOn) { + if (isOn) { + ShowLoad("Generating MFA", "MFA Setup Commencing, please wait"); + + await serviceClient.EnableMFA(this.IdentityMenu.Identity.Identifier); + } else { + this.ShowMFA(IdentityMenu.Identity, 3); + } + + HideLoad(); + } + + /// + /// When a Service Client is ready to setup the MFA Authorization + /// + /// The service client + /// The MFA Event + private void ServiceClient_OnMfaEvent(object sender, MfaEvent mfa) { + HideLoad(); + this.Dispatcher.Invoke(async () => { + if (mfa.Action == "enrollment_challenge") { + string url = HttpUtility.UrlDecode(mfa.ProvisioningUrl); + string secret = HttpUtility.ParseQueryString(url)["secret"]; + this.IdentityMenu.Identity.RecoveryCodes = mfa?.RecoveryCodes?.ToArray(); + SetupMFA(this.IdentityMenu.Identity, url, secret); + } else if (mfa.Action == "auth_challenge") { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsMFANeeded = true; + identities[i].IsTimingOut = false; + break; + } + } + } else if (mfa.Action == "enrollment_verification") { + if (mfa.Successful) { + var found = identities.Find(id => id.Identifier == mfa.Identifier); + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsMFANeeded = false; + identities[i].IsMFAEnabled = true; + identities[i].IsTimingOut = false; + identities[i].LastUpdatedTime = DateTime.Now; + for (int j = 0; j < identities[i].Services.Count; j++) { + identities[i].Services[j].TimeUpdated = DateTime.Now; + identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; + } + found = identities[i]; + found.IsMFAEnabled = true; + break; + } + } + if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; + ShowMFARecoveryCodes(found); + } else { + await ShowBlurbAsync("Provided code could not be verified", ""); + } + } else if (mfa.Action == "enrollment_remove") { + if (mfa.Successful) { + var found = identities.Find(id => id.Identifier == mfa.Identifier); + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsMFAEnabled = false; + identities[i].IsMFANeeded = false; + identities[i].LastUpdatedTime = DateTime.Now; + identities[i].IsTimingOut = false; + for (int j = 0; j < identities[i].Services.Count; j++) { + identities[i].Services[j].TimeUpdated = DateTime.Now; + identities[i].Services[j].TimeoutRemaining = 0; + } + found = identities[i]; + break; + } + } + if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; + await ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", ""); + } else { + await ShowBlurbAsync("MFA Removal Failed", ""); + } + } else if (mfa.Action == "mfa_auth_status") { + var found = identities.Find(id => id.Identifier == mfa.Identifier); + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == mfa.Identifier) { + identities[i].WasNotified = false; + identities[i].WasFullNotified = false; + identities[i].IsTimingOut = false; + identities[i].IsMFANeeded = !mfa.Successful; + identities[i].LastUpdatedTime = DateTime.Now; + for (int j = 0; j < identities[i].Services.Count; j++) { + identities[i].Services[j].TimeUpdated = DateTime.Now; + identities[i].Services[j].TimeoutRemaining = identities[i].Services[j].Timeout; + } + found = identities[i]; + break; + } + } + if (this.IdentityMenu.Identity != null && this.IdentityMenu.Identity.Identifier == mfa.Identifier) this.IdentityMenu.Identity = found; + // ShowBlurb("mfa authenticated: " + mfa.Successful, ""); + } else { + await ShowBlurbAsync("Unexpected error when processing MFA", ""); + logger.Error("unexpected action: " + mfa.Action); + } + + LoadIdentities(true); + }); + } + + /// + /// Show the MFA Setup Modal + /// + /// The Ziti Identity to Setup + public void SetupMFA(ZitiIdentity identity, string url, string secret) { + MFASetup.Opacity = 0; + MFASetup.Visibility = Visibility.Visible; + MFASetup.Margin = new Thickness(0, 0, 0, 0); + MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + MFASetup.ShowSetup(identity, url, secret); + ShowModal(); + } + + /// + /// Show the MFA Authentication Screen when it is time to authenticate + /// + /// The Ziti Identity to Authenticate + public void MFAAuthenticate(ZitiIdentity identity) { + this.ShowMFA(identity, 1); + } + + /// + /// Show MFA for the identity and set the type of screen to show + /// + /// The Identity that is currently active + /// The type of screen to show - 1 Setup, 2 Authenticate, 3 Remove MFA, 4 Regenerate Codes + private void ShowMFA(ZitiIdentity identity, int type) { + MFASetup.Opacity = 0; + MFASetup.Visibility = Visibility.Visible; + MFASetup.Margin = new Thickness(0, 0, 0, 0); + + DoubleAnimation animatin = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); + animatin.Completed += Animatin_Completed; + MFASetup.BeginAnimation(Grid.OpacityProperty, animatin); + MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + + MFASetup.ShowMFA(identity, type); + + ShowModal(); + } + + private void Animatin_Completed(object sender, EventArgs e) { + MFASetup.AuthCode.Focusable = true; + MFASetup.AuthCode.Focus(); + } + + /// + /// Show the MFA Recovery Codes + /// + /// The Ziti Identity to Authenticate + async public void ShowMFARecoveryCodes(ZitiIdentity identity) { + if (identity.IsMFAEnabled) { + if (identity.IsMFAEnabled && identity.RecoveryCodes != null) { + MFASetup.Opacity = 0; + MFASetup.Visibility = Visibility.Visible; + MFASetup.Margin = new Thickness(0, 0, 0, 0); + MFASetup.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + MFASetup.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + + MFASetup.ShowRecovery(identity.RecoveryCodes, identity); + + ShowModal(); + } else { + this.ShowMFA(IdentityMenu.Identity, 2); + } + } else { + await ShowBlurbAsync("MFA is not setup on this Identity", ""); + } + } + + /// + /// Show the modal, aniimating opacity + /// + private void ShowModal() { + ModalBg.Visibility = Visibility.Visible; + ModalBg.Opacity = 0; + DoubleAnimation animation = new DoubleAnimation(.8, TimeSpan.FromSeconds(.3)); + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// Close the various MFA windows + /// + /// The close button + /// The event arguments + private void CloseComplete(object sender, EventArgs e) { + MFASetup.Visibility = Visibility.Collapsed; + } + + /// + /// Hide the modal animating the opacity + /// + private void HideModal() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + animation.Completed += ModalHideComplete; + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// When the animation completes, set the visibility to avoid UI object conflicts + /// + /// The animation + /// The event + private void ModalHideComplete(object sender, EventArgs e) { + ModalBg.Visibility = Visibility.Collapsed; + } + + /// + /// Close the MFA Screen with animation + /// + private void DoClose(bool isComplete) { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + animation.Completed += CloseComplete; + MFASetup.BeginAnimation(Grid.OpacityProperty, animation); + MFASetup.BeginAnimation(Grid.MarginProperty, animateThick); + HideModal(); + if (isComplete) { + if (MFASetup.Type == 1) { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == MFASetup.Identity.Identifier) { + identities[i] = MFASetup.Identity; + identities[i].LastUpdatedTime = DateTime.Now; + } + } + } + } + if (IdentityMenu.IsVisible) { + if (isComplete) { + if (MFASetup.Type == 2) { + ShowRecovery(IdentityMenu.Identity); + } else if (MFASetup.Type == 3) { + } else if (MFASetup.Type == 4) { + ShowRecovery(IdentityMenu.Identity); + } + } + IdentityMenu.UpdateView(); + } + LoadIdentities(true); + } + + private void AddIdentity(ZitiIdentity id) { + semaphoreSlim.Wait(); + if (!identities.Any(i => id.Identifier == i.Identifier)) { + identities.Add(id); + } + semaphoreSlim.Release(); + } + + private System.Windows.Forms.ContextMenu contextMenu; + private System.Windows.Forms.MenuItem contextMenuItem; + private System.ComponentModel.IContainer components; + public MainWindow() { + InitializeComponent(); + NextNotificationTime = DateTime.Now; + SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; + string nlogFile = Path.Combine(ExecutionDirectory, ThisAssemblyName + "-log.config"); + + + ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompat_OnActivated; + + bool byFile = false; + if (File.Exists(nlogFile)) { + LogManager.Configuration = new XmlLoggingConfiguration(nlogFile); + byFile = true; + } else { + var config = new LoggingConfiguration(); + // Targets where to log to: File and Console + var logfile = new FileTarget("logfile") { + FileName = ExpectedLogPathUI, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveNumbering = ArchiveNumberingMode.Rolling, + MaxArchiveFiles = 7, + AutoFlush = true, + Layout = "[${date:format=yyyy-MM-ddTHH:mm:ss.fff}Z] ${level:uppercase=true:padding=5}\t${logger}\t${message}\t${exception:format=tostring}", + }; + var logconsole = new ConsoleTarget("logconsole"); + + // Rules for mapping loggers to targets + config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); + config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); + + // Apply config + LogManager.Configuration = config; + } + logger.Info("============================== UI started =============================="); + logger.Info("logger initialized"); + logger.Info(" - version : {0}", asm.GetName().Version.ToString()); + logger.Info(" - using file: {0}", byFile); + logger.Info(" - file: {0}", nlogFile); + logger.Info("========================================================================"); + + App.Current.MainWindow.WindowState = WindowState.Normal; + App.Current.MainWindow.Deactivated += MainWindow_Deactivated; + App.Current.MainWindow.Activated += MainWindow_Activated; + App.Current.Exit += Current_Exit; + App.Current.SessionEnding += Current_SessionEnding; + + + this.components = new System.ComponentModel.Container(); + this.contextMenu = new System.Windows.Forms.ContextMenu(); + this.contextMenuItem = new System.Windows.Forms.MenuItem(); + this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.contextMenuItem }); + + this.contextMenuItem.Index = 0; + this.contextMenuItem.Text = "&Close UI"; + this.contextMenuItem.Click += new System.EventHandler(this.contextMenuItem_Click); + + + notifyIcon = new System.Windows.Forms.NotifyIcon(); + notifyIcon.Visible = true; + notifyIcon.Click += TargetNotifyIcon_Click; + notifyIcon.Visible = true; + notifyIcon.BalloonTipClosed += NotifyIcon_BalloonTipClosed; + notifyIcon.MouseClick += NotifyIcon_MouseClick; + notifyIcon.ContextMenu = this.contextMenu; + + IdentityMenu.OnDetach += OnDetach; + MainMenu.OnDetach += OnDetach; + + this.MainMenu.MainWindow = this; + this.IdentityMenu.MainWindow = this; + SetNotifyIcon("white"); + + this.PreviewKeyDown += KeyPressed; + MFASetup.OnLoad += MFASetup_OnLoad; + MFASetup.OnError += MFASetup_OnError; + IdentityMenu.OnMessage += IdentityMenu_OnMessage; + } + + async private void MFASetup_OnError(string message) { + await ShowBlurbAsync(message, "", "error"); + } + + private static ToastButton feedbackToastButton = new ToastButton() + .SetContent("Click here to collect logs") + .AddArgument("action", "feedback"); + + private void ToastNotificationManagerCompat_OnActivated(ToastNotificationActivatedEventArgsCompat e) { + this.Dispatcher.Invoke(() => { + if (e.Argument != null && e.Argument.Length > 0) { + string[] items = e.Argument.Split(';'); + if (items.Length > 0) { + string[] values = items[0].Split('='); + if (values.Length == 2) { + string identifier = values[1]; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == identifier) { + ShowMFA(identities[i], 1); + break; + } + } + } + } + } + + ToastArguments args = ToastArguments.Parse(e.Argument); + string value = null; + if (args.TryGetValue("action", out value)) { + this.Dispatcher.Invoke(() => { + MainMenu.CollectFeedbackLogs(e, null); + }); + } + this.Show(); + this.Activate(); + }); + } + + private void KeyPressed(object sender, KeyEventArgs e) { + if (e.Key == Key.Escape) { + if (IdentityMenu.Visibility == Visibility.Visible) IdentityMenu.Visibility = Visibility.Collapsed; + else if (MainMenu.Visibility == Visibility.Visible) MainMenu.Visibility = Visibility.Collapsed; + } + } + + private void MFASetup_OnLoad(bool isComplete, string title, string message) { + if (isComplete) HideLoad(); + else ShowLoad(title, message); + } + + private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e) { + if (notifyIcon != null) { + notifyIcon.Visible = false; + notifyIcon.Icon.Dispose(); + notifyIcon.Dispose(); + notifyIcon = null; + } + Application.Current.Shutdown(); + } + + private void Current_Exit(object sender, ExitEventArgs e) { + if (notifyIcon != null) { + notifyIcon.Visible = false; + if (notifyIcon.Icon != null) { + notifyIcon.Icon.Dispose(); + } + notifyIcon.Dispose(); + notifyIcon = null; + } + } + + private void contextMenuItem_Click(object Sender, EventArgs e) { + Application.Current.Shutdown(); + } + + private void NotifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { + if (e.Button == System.Windows.Forms.MouseButtons.Left) { + System.Windows.Forms.MouseEventArgs mea = (System.Windows.Forms.MouseEventArgs)e; + this.Show(); + this.Activate(); + //Do the awesome left clickness + } else if (e.Button == System.Windows.Forms.MouseButtons.Right) { + //Do the wickedy right clickness + } else { + //Some other button from the enum :) + } + } + + private void NotifyIcon_BalloonTipClosed(object sender, EventArgs e) { + var thisIcon = (System.Windows.Forms.NotifyIcon)sender; + thisIcon.Visible = false; + thisIcon.Dispose(); + } + + private void Window_MouseDown(object sender, MouseButtonEventArgs e) { + OnDetach(e); + } + + private void OnDetach(MouseButtonEventArgs e) { + if (e.ChangedButton == MouseButton.Left) { + _isAttached = false; + IdentityMenu.Arrow.Visibility = Visibility.Collapsed; + Arrow.Visibility = Visibility.Collapsed; + MainMenu.Detach(); + this.DragMove(); + } + } + + private void MainWindow_Activated(object sender, EventArgs e) { + Placement(); + this.Show(); + this.Visibility = Visibility.Visible; + this.Opacity = 1; + } + + private void MainWindow_Deactivated(object sender, EventArgs e) { + if (this._isAttached) { + this.Visibility = Visibility.Hidden; + } + } + + private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + if (notifyIcon != null) { + notifyIcon.Visible = false; + notifyIcon.Icon.Dispose(); + notifyIcon.Dispose(); + notifyIcon = null; + } + Application.Current.Shutdown(); + } + + private void SetCantDisplay(string title, string detailMessage, Visibility closeButtonVisibility) { + this.Dispatcher.Invoke(() => { + NoServiceView.Visibility = Visibility.Visible; + CloseErrorButton.IsEnabled = true; + CloseErrorButton.Visibility = closeButtonVisibility; + ErrorMsg.Content = title; + ErrorMsgDetail.Content = detailMessage; + SetNotifyIcon("red"); + _isServiceInError = true; + UpdateServiceView(); + }); + } + + private void TargetNotifyIcon_Click(object sender, EventArgs e) { + this.Show(); + this.Activate(); + Application.Current.MainWindow.Activate(); + } + + private void UpdateServiceView() { + if (_isServiceInError) { + AddIdAreaButton.Opacity = 0.1; + AddIdAreaButton.IsEnabled = false; + AddIdButton.Opacity = 0.1; + AddIdButton.IsEnabled = false; + ConnectButton.Opacity = 0.1; + StatArea.Opacity = 0.1; + } else { + AddIdAreaButton.Opacity = 1.0; + AddIdAreaButton.IsEnabled = true; + AddIdButton.Opacity = 1.0; + AddIdButton.IsEnabled = true; + StatArea.Opacity = 1.0; + ConnectButton.Opacity = 1.0; + } + TunnelConnected(!_isServiceInError); + } + + private void App_ReceiveString(string obj) { + Console.WriteLine(obj); + this.Show(); + this.Activate(); + } + + async private void MainWindow_Loaded(object sender, RoutedEventArgs e) { + + Window window = Window.GetWindow(App.Current.MainWindow); + ZitiDesktopEdge.App app = (ZitiDesktopEdge.App)App.Current; + app.ReceiveString += App_ReceiveString; + + // add a new service client + serviceClient = new DataClient("ui"); + serviceClient.OnClientConnected += ServiceClient_OnClientConnected; + serviceClient.OnClientDisconnected += ServiceClient_OnClientDisconnected; + serviceClient.OnIdentityEvent += ServiceClient_OnIdentityEvent; + serviceClient.OnMetricsEvent += ServiceClient_OnMetricsEvent; + serviceClient.OnServiceEvent += ServiceClient_OnServiceEvent; + serviceClient.OnTunnelStatusEvent += ServiceClient_OnTunnelStatusEvent; + serviceClient.OnMfaEvent += ServiceClient_OnMfaEvent; + serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; + serviceClient.OnBulkServiceEvent += ServiceClient_OnBulkServiceEvent; + serviceClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; + serviceClient.OnControllerEvent += ServiceClient_OnControllerEvent; + Application.Current.Properties.Add("ServiceClient", serviceClient); + + monitorClient = new MonitorClient("ui"); + monitorClient.OnClientConnected += MonitorClient_OnClientConnected; + monitorClient.OnNotificationEvent += MonitorClient_OnInstallationNotificationEvent; + monitorClient.OnServiceStatusEvent += MonitorClient_OnServiceStatusEvent; + monitorClient.OnShutdownEvent += MonitorClient_OnShutdownEvent; + monitorClient.OnCommunicationError += MonitorClient_OnCommunicationError; + monitorClient.OnReconnectFailure += MonitorClient_OnReconnectFailure; + Application.Current.Properties.Add("MonitorClient", monitorClient); + + Application.Current.Properties.Add("Identities", new List()); + MainMenu.OnAttachmentChange += AttachmentChanged; + MainMenu.OnLogLevelChanged += LogLevelChanged; + MainMenu.OnShowBlurb += MainMenu_OnShowBlurb; + IdentityMenu.OnError += IdentityMenu_OnError; + + try { + await serviceClient.ConnectAsync(); + await serviceClient.WaitForConnectionAsync(); + } catch /*ignored for now (Exception ex) */ + { + ShowServiceNotStarted(); + serviceClient.Reconnect(); + } + + try { + await monitorClient.ConnectAsync(); + await monitorClient.WaitForConnectionAsync(); + } catch /*ignored for now (Exception ex) */ + { + monitorClient.Reconnect(); + } + + IdentityMenu.OnForgot += IdentityForgotten; + Placement(); + } + + private void MonitorClient_OnCommunicationError(object sender, Exception e) { + string msg = "Communication Error with monitor?"; + ShowError(msg, e.Message); + } + + private void MainMenu_OnShowBlurb(string message) { + _ = ShowBlurbAsync(message, "", "info"); + } + + private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) { + var found = identities.Find(id => id.Identifier == e.Identifier); + if (found == null) { + logger.Warn($"{e.Action} service event for {e.Identifier} but the provided identity identifier was not found!"); + return; + } else { + if (e.RemovedServices != null) { + foreach (var removed in e.RemovedServices) { + removeService(found, removed); + } + } + if (e.AddedServices != null) { + foreach (var added in e.AddedServices) { + addService(found, added); + } + } + LoadIdentities(true); + this.Dispatcher.Invoke(() => { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + } + + private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { + var displayMFARequired = false; + var displayMFATimout = false; + foreach (var notification in e.Notification) { + var found = identities.Find(id => id.Identifier == notification.Identifier); + if (found == null) { + logger.Warn($"{e.Op} event for {notification.Identifier} but the provided identity identifier was not found!"); + continue; + } else { + found.TimeoutMessage = notification.Message; + found.MaxTimeout = notification.MfaMaximumTimeout; + found.MinTimeout = notification.MfaMinimumTimeout; + + if (notification.MfaMinimumTimeout == 0) { + // display mfa token icon + displayMFARequired = true; + } else { + displayMFATimout = true; + } + + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + } + } + + // we may need to display mfa icon, based on the timer in UI, remove found.MFAInfo.ShowMFA setting in this function. + // the below function can show mfa icon even after user authenticates successfully, in race conditions + if (displayMFARequired || displayMFATimout) { + this.Dispatcher.Invoke(() => { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + LoadIdentities(true); + } + + private void ServiceClient_OnControllerEvent(object sender, ControllerEvent e) { + logger.Debug($"==== ControllerEvent : action:{e.Action} identifier:{e.Identifier}"); + // commenting this block, because when it receives the disconnected events, identities are disabled and + // it is not allowing me to click/perform any operation on the identity + // the color of the title is also too dark, and it is not clearly visible, when the identity is disconnected + /* if (e.Action == "connected") { + var found = identities.Find(i => i.Identifier == e.Identifier); + found.IsConnected = true; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } else if (e.Action == "disconnected") { + var found = identities.Find(i => i.Identifier == e.Identifier); + found.IsConnected = false; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } */ + } + + + string nextVersionStr = null; + private void MonitorClient_OnReconnectFailure(object sender, object e) { + logger.Trace("OnReconnectFailure triggered"); + if (nextVersionStr == null) { + // check for the current version + nextVersionStr = "checking for update"; + Version nextVersion = GithubAPI.GetVersion(GithubAPI.GetJson(GithubAPI.ProdUrl)); + nextVersionStr = nextVersion.ToString(); + Version currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; //fetch from ziti? + + int compare = currentVersion.CompareTo(nextVersion); + if (compare < 0) { + MainMenu.SetAppUpgradeAvailableText("Upgrade available: " + nextVersionStr); + logger.Info("upgrade is available. Published version: {} is newer than the current version: {}", nextVersion, currentVersion); + //UpgradeAvailable(); + } else if (compare > 0) { + logger.Info("the version installed: {0} is newer than the released version: {1}", currentVersion, nextVersion); + MainMenu.SetAppIsNewer("This version is newer than the latest: " + nextVersionStr); + } else { + logger.Info("Current version installed: {0} is the same as the latest released version {1}", currentVersion, nextVersion); + MainMenu.SetAppUpgradeAvailableText(""); + } + } + } + + private void MonitorClient_OnShutdownEvent(object sender, StatusEvent e) { + logger.Info("The monitor has indicated the application should shut down."); + this.Dispatcher.Invoke(() => { + Application.Current.Shutdown(); + }); + } + + private void MonitorClient_OnServiceStatusEvent(object sender, MonitorServiceStatusEvent evt) { + this.Dispatcher.Invoke(() => { + try { + if (evt.Message?.ToLower() == "upgrading") { + logger.Info("The monitor has indicated an upgrade is in progress. Shutting down the UI"); + UpgradeSentinel.StartUpgradeSentinel(); + + App.Current.Exit -= Current_Exit; + logger.Info("Removed Current_Exit handler"); + notifyIcon.Visible = false; + notifyIcon.Icon.Dispose(); + notifyIcon.Dispose(); + Application.Current.Shutdown(); + return; + } + SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); + if (evt.Code != 0) { + logger.Error("CODE: " + evt.Code); + if (MainMenu.ShowUnexpectedFailure) { + ShowToast("The data channel has stopped unexpectedly", $"If this keeps happening please collect logs and report the issue.", feedbackToastButton); + } + } + MainMenu.ShowUpdateAvailable(); + logger.Debug("MonitorClient_OnServiceStatusEvent: {0}", evt.Status); + Application.Current.Properties["ReleaseStream"] = evt.ReleaseStream; + + ServiceControllerStatus status = (ServiceControllerStatus)Enum.Parse(typeof(ServiceControllerStatus), evt.Status); + + switch (status) { + case ServiceControllerStatus.Running: + logger.Info("Service is started"); + break; + case ServiceControllerStatus.Stopped: + logger.Info("Service is stopped"); + ShowServiceNotStarted(); + break; + case ServiceControllerStatus.StopPending: + logger.Info("Service is stopping..."); + + this.Dispatcher.Invoke(async () => { + SetCantDisplay("The Service is Stopping", "Please wait while the service stops", Visibility.Visible); + await WaitForServiceToStop(DateTime.Now + TimeSpan.FromSeconds(30)); + }); + break; + case ServiceControllerStatus.StartPending: + logger.Info("Service is starting..."); + break; + case ServiceControllerStatus.PausePending: + logger.Warn("UNEXPECTED STATUS: PausePending"); + break; + case ServiceControllerStatus.Paused: + logger.Warn("UNEXPECTED STATUS: Paused"); + break; + default: + logger.Warn("UNEXPECTED STATUS: {0}", evt.Status); + break; + } + } catch (Exception ex) { + logger.Warn(ex, "unexpected exception in MonitorClient_OnServiceStatusEvent? {0}", ex.Message); + } + }); + } + + public void SetAutomaticUpdateEnabled(string enabled, string url) { + this.Dispatcher.Invoke(() => { + state.AutomaticUpdatesDisabled = bool.Parse(enabled); + state.AutomaticUpdateURL = url; + }); + } + + private void MonitorClient_OnInstallationNotificationEvent(object sender, InstallationNotificationEvent evt) { + this.Dispatcher.Invoke(() => { + logger.Debug("MonitorClient_OnInstallationNotificationEvent: {0}", evt.Message); + switch (evt.Message?.ToLower()) { + case "installationupdate": + logger.Debug("Installation Update is available - {0}", evt.ZDEVersion); + var remaining = evt.InstallTime - DateTime.Now; + + state.PendingUpdate.Version = evt.ZDEVersion; + state.PendingUpdate.InstallTime = evt.InstallTime; + state.UpdateAvailable = true; + SetAutomaticUpdateEnabled(evt.AutomaticUpgradeDisabled, evt.AutomaticUpgradeURL); + MainMenu.ShowUpdateAvailable(); + AlertCanvas.Visibility = Visibility.Visible; + + if (isToastEnabled()) { + if (!state.AutomaticUpdatesDisabled) { + if (remaining.TotalSeconds < 60) { + //this is an immediate update - show a different message + ShowToast("Ziti Desktop Edge will initiate auto installation in the next minute!"); + } else { + if (DateTime.Now > NextNotificationTime) { + ShowToast($"Update {evt.ZDEVersion} is available for Ziti Desktop Edge and will be automatically installed by " + evt.InstallTime); + NextNotificationTime = DateTime.Now + evt.NotificationDuration; + } else { + logger.Debug("Skipping notification. Time until next notification {} seconds which is at {}", (int)((NextNotificationTime - DateTime.Now).TotalSeconds), NextNotificationTime); + } + } + } else { + ShowToast("New version available", $"Version {evt.ZDEVersion} is available for Ziti Desktop Edge", null); + } + SetNotifyIcon(""); + // display a tag in UI and a button for the update software + } + break; + case "configuration changed": + break; + default: + logger.Debug("unexpected event type?"); + break; + } + }); + } + + private bool isToastEnabled() { + bool result; + //only show notifications once if automatic updates are disabled + if (NotificationsShownCount == 0) { + result = true; //regardless - if never notified, always return true + } else { + result = !state.AutomaticUpdatesDisabled; + } + return result; + } + + private void ShowToast(string header, string message, ToastButton button) { + try { + logger.Debug("showing toast: {} {}", header, message); + var builder = new ToastContentBuilder() + .AddArgument("notbutton", "click") + .AddText(header) + .AddText(message); + if (button != null) { + builder.AddButton(button); + } + builder.Show(); + NotificationsShownCount++; + } catch { + logger.Warn("couldn't show toast: {} {}", header, message); + } + } + + + private void ShowToast(string message) { + ShowToast("Important Notice", message, null); + } + + async private Task WaitForServiceToStop(DateTime until) { + //continually poll for the service to stop. If it is stuck - ask the user if they want to try to force + //close the service + while (DateTime.Now < until) { + await Task.Delay(250); + MonitorServiceStatusEvent resp = await monitorClient.StatusAsync(); + if (resp.IsStopped()) { + // good - that's what we are waiting for... + return; + } else { + // bad - not stopped yet... + logger.Debug("Waiting for service to stop... Still not stopped yet. Status: {0}", resp.Status); + } + } + // real bad - means it's stuck probably. Ask the user if they want to try to force it... + logger.Warn("Waiting for service to stop... Service did not reach stopped state in the expected amount of time."); + SetCantDisplay("The Service Appears Stuck", "Would you like to try to force close the service?", Visibility.Visible); + CloseErrorButton.Content = "Force Quit"; + CloseErrorButton.Click -= CloseError; + CloseErrorButton.Click += ForceQuitButtonClick; + } + + async private void ForceQuitButtonClick(object sender, RoutedEventArgs e) { + MonitorServiceStatusEvent status = await monitorClient.ForceTerminateAsync(); + if (status.IsStopped()) { + //good + CloseErrorButton.Click += CloseError; //reset the close button... + CloseErrorButton.Click -= ForceQuitButtonClick; + } else { + //bad... + SetCantDisplay("The Service Is Still Running", "Current status is: " + status.Status, Visibility.Visible); + } + } + + async private void StartZitiService(object sender, RoutedEventArgs e) { + try { + ShowLoad("Starting", "Starting the data service"); + logger.Info("StartZitiService"); + var r = await monitorClient.StartServiceAsync(); + if (r.Code != 0) { + logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); + } else { + logger.Info("Service started!"); + CloseErrorButton.Click -= StartZitiService; + CloseError(null, null); + } + } catch (MonitorServiceException me) { + logger.Warn("the monitor service appears offline. {0}", me); + CloseErrorButton.IsEnabled = true; + HideLoad(); + ShowError("Error Starting Service", "The monitor service is offline"); + } catch (Exception ex) { + logger.Error(ex, "UNEXPECTED ERROR!"); + CloseErrorButton.IsEnabled = true; + HideLoad(); + ShowError("Unexpected Error", "Code 2:" + ex.Message); + } + CloseErrorButton.IsEnabled = true; + // HideLoad(); + } + + private void ShowServiceNotStarted() { + TunnelConnected(false); + LoadIdentities(true); + } + + private void MonitorClient_OnClientConnected(object sender, object e) { + logger.Debug("MonitorClient_OnClientConnected"); + MainMenu.SetAppUpgradeAvailableText(""); + } + + async private Task LogLevelChanged(string level) { + int logsSet = 0; + try { + await serviceClient.SetLogLevelAsync(level); + logsSet++; + await monitorClient.SetLogLevelAsync(level); + logsSet++; + Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(level); + return true; + } catch (Exception ex) { + logger.Error(ex, "Unexpected error. logsSet: {0}", logsSet); + if (logsSet > 1) { + await ShowBlurbAsync("Unexpected error setting logs?", ""); + } else if (logsSet > 0) { + await ShowBlurbAsync("Failed to set monitor client log level", ""); + } else { + await ShowBlurbAsync("Failed to set log levels", ""); + } + } + return false; + } + + private void IdentityMenu_OnError(string message) { + ShowError("Identity Error", message); + } + + private void ServiceClient_OnClientConnected(object sender, object e) { + this.Dispatcher.Invoke(() => { + MainMenu.Connected(); + NoServiceView.Visibility = Visibility.Collapsed; + _isServiceInError = false; + UpdateServiceView(); + SetNotifyIcon("white"); + LoadIdentities(true); + }); + } + + private void ServiceClient_OnClientDisconnected(object sender, object e) { + this.Dispatcher.Invoke(() => { + IdentityMenu.Visibility = Visibility.Collapsed; + MFASetup.Visibility = Visibility.Collapsed; + HideModal(); + MainMenu.Disconnected(); + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = (IdentityItem)IdList.Children[i]; + item.StopTimers(); + } + IdList.Children.Clear(); + if (e != null) { + logger.Debug(e.ToString()); + } + //SetCantDisplay("Start the Ziti Tunnel Service to continue"); + SetNotifyIcon("red"); + ShowServiceNotStarted(); + }); + } + + /// + /// If an identity gets added late, execute this. + /// + /// Do not update services for identity events + /// + /// The sending service + /// The identity event + private void ServiceClient_OnIdentityEvent(object sender, IdentityEvent e) { + if (e == null) return; + + ZitiIdentity zid = ZitiIdentity.FromClient(e.Id); + logger.Debug($"==== IdentityEvent : action:{e.Action} identifer:{e.Id.Identifier} name:{e.Id.Name} "); + + this.Dispatcher.Invoke(async () => { + if (e.Action == "added") { + var found = identities.Find(i => i.Identifier == e.Id.Identifier); + if (found == null) { + AddIdentity(zid); + LoadIdentities(true); + } else { + // means we likely are getting an update for some reason. compare the identities and use the latest info + if (zid.Name != null && zid.Name.Length > 0) found.Name = zid.Name; + if (zid.ControllerUrl != null && zid.ControllerUrl.Length > 0) found.ControllerUrl = zid.ControllerUrl; + if (zid.ContollerVersion != null && zid.ContollerVersion.Length > 0) found.ContollerVersion = zid.ContollerVersion; + found.IsEnabled = zid.IsEnabled; + found.IsMFAEnabled = e.Id.MfaEnabled; + found.IsConnected = true; + found.NeedsExtAuth = e.Id.NeedsExtAuth; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } + } else if (e.Action == "updated") { + //this indicates that all updates have been sent to the UI... wait for 2 seconds then trigger any ui updates needed + await Task.Delay(2000); + LoadIdentities(true); + } else if (e.Action == "connected") { + var found = identities.Find(i => i.Identifier == e.Id.Identifier); + found.IsConnected = true; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } else if (e.Action == "disconnected") { + var found = identities.Find(i => i.Identifier == e.Id.Identifier); + found.IsConnected = false; + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == found.Identifier) { + identities[i] = found; + break; + } + } + LoadIdentities(true); + } else if (e.Action == "needs_ext_login") { + logger.Debug("needs_ext_login action received"); //handled through identity event at the moment (forever?) + } else { + logger.Warn("unexpected action received: {}", e.Action); + IdentityForgotten(ZitiIdentity.FromClient(e.Id)); + } + }); + logger.Debug($"IDENTITY EVENT. Action: {e.Action} identifier: {zid.Identifier}"); + } + + private void ServiceClient_OnMetricsEvent(object sender, List ids) { + if (ids != null) { + long totalUp = 0; + long totalDown = 0; + foreach (var id in ids) { + //logger.Debug($"==== MetricsEvent : id {id.Name} down: {id.Metrics.Down} up:{id.Metrics.Up}"); + if (id?.Metrics != null) { + totalDown += id.Metrics.Down; + totalUp += id.Metrics.Up; + } + } + this.Dispatcher.Invoke(() => { + SetSpeed(totalUp, UploadSpeed, UploadSpeedLabel); + SetSpeed(totalDown, DownloadSpeed, DownloadSpeedLabel); + }); + } + } + + public void SetSpeed(decimal bytes, Label speed, Label speedLabel) { + int counter = 0; + while (Math.Round(bytes / 1024) >= 1) { + bytes = bytes / 1024; + counter++; + } + speed.Content = bytes.ToString("0.0"); + speedLabel.Content = suffixes[counter]; + } + + private void ServiceClient_OnServiceEvent(object sender, ServiceEvent e) { + if (e == null) return; + + logger.Debug($"==== ServiceEvent : action:{e.Action} identifier:{e.Identifier} name:{e.Service.Name} "); + var found = identities.Find(id => id.Identifier == e.Identifier); + if (found == null) { + logger.Debug($"{e.Action} service event for {e.Service.Name} but the provided identity identifier {e.Identifier} is not found!"); + return; + } + + if (e.Action == "added") { + addService(found, e.Service); + } else { + removeService(found, e.Service); + } + LoadIdentities(true); + this.Dispatcher.Invoke(() => { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + + private void addService(ZitiIdentity found, Service added) { + ZitiService zs = new ZitiService(added); + var svc = found.Services.Find(s => s.Name == zs.Name); + if (svc == null) { + logger.Debug("Service Added: " + zs.Name); + found.Services.Add(zs); + if (zs.HasFailingPostureCheck()) { + found.HasServiceFailingPostureCheck = true; + if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) { + found.IsMFANeeded = true; + } + } + } else { + logger.Debug("the service named " + zs.Name + " is already accounted for on this identity."); + } + } + + private void removeService(ZitiIdentity found, Service removed) { + logger.Debug("removing the service named: {0}", removed.Name); + found.Services.RemoveAll(s => s.Name == removed.Name); + } + + private void ServiceClient_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { + if (e == null) return; //just skip it for now... + logger.Debug($"==== TunnelStatusEvent: "); + Application.Current.Properties.Remove("CurrentTunnelStatus"); + Application.Current.Properties.Add("CurrentTunnelStatus", e.Status); + e.Status.Dump(Console.Out); + this.Dispatcher.Invoke(() => { + /*if (e.ApiVersion != DataClient.EXPECTED_API_VERSION) { + SetCantDisplay("Version mismatch!", "The version of the Service is not compatible", Visibility.Visible); + return; + }*/ + this.MainMenu.LogLevel = e.Status.LogLevel; + Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.Status.LogLevel); + InitializeTimer((int)e.Status.Duration); + LoadStatusFromService(e.Status); + LoadIdentities(true); + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + + private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { + if (e.LogLevel != null) { + SetLogLevel_monitor(e.LogLevel); + this.Dispatcher.Invoke(() => { + this.MainMenu.LogLevel = e.LogLevel; + Ziti.Desktop.Edge.Utils.UIUtils.SetLogLevel(e.LogLevel); + }); + } + } + + async private void SetLogLevel_monitor(string loglevel) { + await monitorClient.SetLogLevelAsync(loglevel); + } + + private void IdentityForgotten(ZitiIdentity forgotten) { + ZitiIdentity idToRemove = null; + foreach (var id in identities) { + if (id.Identifier == forgotten.Identifier) { + idToRemove = id; + break; + } + } + identities.Remove(idToRemove); + LoadIdentities(false); + } + + private void AttachmentChanged(bool attached) { + _isAttached = attached; + if (!_isAttached) { + SetLocation(); + } + Placement(); + MainMenu.Visibility = Visibility.Collapsed; + } + + private void LoadStatusFromService(TunnelStatus status) { + //clear any identities + this.identities.Clear(); + + if (status != null) { + _isServiceInError = false; + UpdateServiceView(); + NoServiceView.Visibility = Visibility.Collapsed; + SetNotifyIcon("green"); + if (!Application.Current.Properties.Contains("ip")) { + Application.Current.Properties.Add("ip", status?.IpInfo?.Ip); + } else { + Application.Current.Properties["ip"] = status?.IpInfo?.Ip; + } + if (!Application.Current.Properties.Contains("subnet")) { + Application.Current.Properties.Add("subnet", status?.IpInfo?.Subnet); + } else { + Application.Current.Properties["subnet"] = status?.IpInfo?.Subnet; + } + if (!Application.Current.Properties.Contains("mtu")) { + Application.Current.Properties.Add("mtu", status?.IpInfo?.MTU); + } else { + Application.Current.Properties["mtu"] = status?.IpInfo?.MTU; + } + if (!Application.Current.Properties.Contains("dns")) { + Application.Current.Properties.Add("dns", status?.IpInfo?.DNS); + } else { + Application.Current.Properties["dns"] = status?.IpInfo?.DNS; + } + if (!Application.Current.Properties.Contains("dnsenabled")) { + Application.Current.Properties.Add("dnsenabled", status?.AddDns); + } else { + Application.Current.Properties["dnsenabled"] = status?.AddDns; + } + + string key = "ApiPageSize"; + if (!Application.Current.Properties.Contains(key)) { + Application.Current.Properties.Add(key, status?.ApiPageSize); + } else { + Application.Current.Properties[key] = status?.ApiPageSize; + } + + foreach (var id in status.Identities) { + updateViewWithIdentity(id); + } + LoadIdentities(true); + } else { + ShowServiceNotStarted(); + } + } + + private void updateViewWithIdentity(Identity id) { + var zid = ZitiIdentity.FromClient(id); + foreach (var i in identities) { + if (i.Identifier == zid.Identifier) { + identities.Remove(i); + break; + } + } + identities.Add(zid); + } + + private bool IsTimingOut() { + if (identities != null) { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].IsTimingOut) return true; + } + } + return false; + } + + private bool IsTimedOut() { + if (identities != null) { + return identities.Any(i => i.IsTimedOut); + } + return false; + } + + private void SetNotifyIcon(string iconPrefix) { + if (iconPrefix != "") CurrentIcon = iconPrefix; + string icon = "pack://application:,,/Assets/Images/ziti-" + CurrentIcon; + if (state.UpdateAvailable) { + icon += "-update"; + } else { + if (IsTimedOut()) { + icon += "-mfa"; + } else { + if (IsTimingOut()) { + icon += "-timer"; + } + } + } + icon += ".ico"; + var iconUri = new Uri(icon); + Stream iconStream = Application.GetResourceStream(iconUri).Stream; + notifyIcon.Icon = new Icon(iconStream); + + Application.Current.MainWindow.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconUri); + } + + private void LoadIdentities(Boolean repaint) { + this.Dispatcher.Invoke(() => { + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = (IdentityItem)IdList.Children[i]; + item.StopTimers(); + } + IdList.Children.Clear(); + IdList.Height = 0; + var desktopWorkingArea = SystemParameters.WorkArea; + if (_maxHeight > (desktopWorkingArea.Height - 10)) _maxHeight = desktopWorkingArea.Height - 10; + if (_maxHeight < 100) _maxHeight = 100; + IdList.MaxHeight = _maxHeight - 520; + ZitiIdentity[] ids = identities.OrderBy(i => (i.Name != null) ? i.Name.ToLower() : i.Name).ToArray(); + MainMenu.SetupIdList(ids); + if (ids.Length > 0 && serviceClient.Connected) { + double height = defaultHeight + (ids.Length * 60); + if (height > _maxHeight) height = _maxHeight; + this.Height = height; + IdentityMenu.SetHeight(this.Height - 160); + MainMenu.IdentitiesButton.Visibility = Visibility.Visible; + foreach (var id in ids) { + IdentityItem idItem = new IdentityItem(); + + idItem.ToggleStatus.IsEnabled = id.IsEnabled; + if (id.IsEnabled) idItem.ToggleStatus.Content = "ENABLED"; + else idItem.ToggleStatus.Content = "DISABLED"; + + idItem.Authenticate += IdItem_Authenticate; + idItem.OnStatusChanged += Id_OnStatusChanged; + idItem.Identity = id; + idItem.IdentityChanged += IdItem_IdentityChanged; + + if (repaint) { + idItem.RefreshUI(); + } + + IdList.Children.Add(idItem); + + if (IdentityMenu.Visibility == Visibility.Visible) { + if (id.Identifier == IdentityMenu.Identity.Identifier) IdentityMenu.Identity = id; + } + } + DoubleAnimation animation = new DoubleAnimation((double)(ids.Length * 64), TimeSpan.FromSeconds(.2)); + IdList.BeginAnimation(FrameworkElement.HeightProperty, animation); + IdListScroller.Visibility = Visibility.Visible; + } else { + this.Height = defaultHeight; + MainMenu.IdentitiesButton.Visibility = Visibility.Collapsed; + IdListScroller.Visibility = Visibility.Visible; + + } + AddIdButton.Visibility = Visibility.Visible; + AddIdAreaButton.Visibility = Visibility.Visible; + + Placement(); + SetNotifyIcon(""); + }); + } + + private void IdItem_IdentityChanged(ZitiIdentity identity) { + for (int i = 0; i < identities.Count; i++) { + if (identities[i].Identifier == identity.Identifier) { + identities[i] = identity; + break; + } + } + SetNotifyIcon(""); + } + + private void IdItem_Authenticate(ZitiIdentity identity) { + ShowAuthenticate(identity); + } + + private void Id_OnStatusChanged(bool attached) { + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = IdList.Children[i] as IdentityItem; + if (item.ToggleSwitch.Enabled) break; + } + } + + private void TunnelConnected(bool isConnected) { + this.Dispatcher.Invoke(() => { + if (isConnected) { + ConnectButton.Visibility = Visibility.Collapsed; + DisconnectButton.Visibility = Visibility.Visible; + MainMenu.Connected(); + HideLoad(); + SetNotifyIcon("green"); + } else { + ConnectButton.Visibility = Visibility.Visible; + DisconnectButton.Visibility = Visibility.Collapsed; + IdentityMenu.Visibility = Visibility.Collapsed; + MainMenu.Visibility = Visibility.Collapsed; + HideBlurb(); + MainMenu.Disconnected(); + DownloadSpeed.Content = "0.0"; + UploadSpeed.Content = "0.0"; + } + }); + } + + private void SetLocation() { + var desktopWorkingArea = SystemParameters.WorkArea; + + var height = MainView.ActualHeight; + IdentityMenu.MainHeight = MainView.ActualHeight; + MainMenu.MainHeight = MainView.ActualHeight; + + Rectangle trayRectangle = WinAPI.GetTrayRectangle(); + if (trayRectangle.Top < 20) { + this.Position = "Top"; + this.Top = desktopWorkingArea.Top + _top; + this.Left = desktopWorkingArea.Right - this.Width - _right; + Arrow.SetValue(Canvas.TopProperty, (double)0); + Arrow.SetValue(Canvas.LeftProperty, (double)185); + MainMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, (double)0); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + } else if (trayRectangle.Left < 20) { + this.Position = "Left"; + this.Left = _left; + this.Top = desktopWorkingArea.Bottom - this.ActualHeight - 75; + Arrow.SetValue(Canvas.TopProperty, height - 200); + Arrow.SetValue(Canvas.LeftProperty, (double)0); + MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 200); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)0); + } else if (desktopWorkingArea.Right == (double)trayRectangle.Left) { + this.Position = "Right"; + this.Left = desktopWorkingArea.Right - this.Width - 20; + this.Top = desktopWorkingArea.Bottom - height - 75; + Arrow.SetValue(Canvas.TopProperty, height - 100); + Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); + MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 100); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, this.Width - 30); + } else { + this.Position = "Bottom"; + this.Left = desktopWorkingArea.Right - this.Width - 75; + this.Top = desktopWorkingArea.Bottom - height; + Arrow.SetValue(Canvas.TopProperty, height - 35); + Arrow.SetValue(Canvas.LeftProperty, (double)185); + MainMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); + MainMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + IdentityMenu.Arrow.SetValue(Canvas.TopProperty, height - 35); + IdentityMenu.Arrow.SetValue(Canvas.LeftProperty, (double)185); + } + } + public void Placement() { + if (_isAttached) { + Arrow.Visibility = Visibility.Visible; + IdentityMenu.Arrow.Visibility = Visibility.Visible; + SetLocation(); + } else { + IdentityMenu.Arrow.Visibility = Visibility.Collapsed; + Arrow.Visibility = Visibility.Collapsed; + } + } + + private void OpenIdentity(ZitiIdentity identity) { + IdentityMenu.Identity = identity; + } + + private void ShowMenu(object sender, MouseButtonEventArgs e) { + MainMenu.Visibility = Visibility.Visible; + } + + async private void AddIdentity(object sender, MouseButtonEventArgs e) { + UIModel.HideOnLostFocus = false; + Microsoft.Win32.OpenFileDialog jwtDialog = new Microsoft.Win32.OpenFileDialog(); + UIModel.HideOnLostFocus = true; + jwtDialog.DefaultExt = ".jwt"; + jwtDialog.Filter = "Ziti Identities (*.jwt)|*.jwt"; + + if (jwtDialog.ShowDialog() == true) { + ShowLoad("Adding Identity", "Please wait while the identity is added"); + string fileContent = File.ReadAllText(jwtDialog.FileName); + + try { + Identity createdId = await serviceClient.AddIdentityAsync(System.IO.Path.GetFileName(jwtDialog.FileName), false, fileContent); + + if (createdId != null) { + var zid = ZitiIdentity.FromClient(createdId); + AddIdentity(zid); + LoadIdentities(true); + await serviceClient.IdentityOnOffAsync(createdId.Identifier, true); + }/* else { + ShowError("Identity Error", "Identity Id was null, please try again"); + }*/ + } catch (ServiceException se) { + ShowError(se.Message, se.AdditionalInfo); + } catch (Exception ex) { + ShowError("Unexpected Error", "Code 2:" + ex.Message); + } + HideLoad(); + } + } + + private void OnTimedEvent(object sender, EventArgs e) { + TimeSpan span = (DateTime.Now - _startDate); + int hours = span.Hours; + int minutes = span.Minutes; + int seconds = span.Seconds; + var hoursString = (hours > 9) ? hours.ToString() : "0" + hours; + var minutesString = (minutes > 9) ? minutes.ToString() : "0" + minutes; + var secondsString = (seconds > 9) ? seconds.ToString() : "0" + seconds; + ConnectedTime.Content = hoursString + ":" + minutesString + ":" + secondsString; + } + + private void InitializeTimer(int millisAgoStarted) { + _startDate = DateTime.Now.Subtract(new TimeSpan(0, 0, 0, 0, millisAgoStarted)); + _tunnelUptimeTimer = new System.Windows.Forms.Timer(); + _tunnelUptimeTimer.Interval = 100; + _tunnelUptimeTimer.Tick += OnTimedEvent; + _tunnelUptimeTimer.Enabled = true; + _tunnelUptimeTimer.Start(); + } + + async private Task DoConnectAsync() { + try { + SetNotifyIcon("green"); + TunnelConnected(true); + + for (int i = 0; i < identities.Count; i++) { + await serviceClient.IdentityOnOffAsync(identities[i].Identifier, true); + } + for (int i = 0; i < IdList.Children.Count; i++) { + IdentityItem item = IdList.Children[i] as IdentityItem; + item._identity.IsEnabled = true; + item.RefreshUI(); + } + } catch (ServiceException se) { + ShowError("Error Occurred", se.Message + " " + se.AdditionalInfo); + } catch (Exception ex) { + ShowError("Unexpected Error", "Code 3:" + ex.Message); + } + } + + async private void Disconnect(object sender, RoutedEventArgs e) { + try { + ShowLoad("Disabling Service", "Please wait for the service to stop."); + var r = await monitorClient.StopServiceAsync(); + if (r.Code != 0) { + logger.Warn("ERROR: Error:{0}, Message:{1}", r.Error, r.Message); + } else { + logger.Info("Service stopped!"); + SetNotifyIcon("white"); + } + } catch (MonitorServiceException me) { + logger.Warn("the monitor service appears offline. {0}", me); + ShowError("Error Disabling Service", "The monitor service is offline"); + } catch (Exception ex) { + logger.Error(ex, "unexpected error: {0}", ex.Message); + ShowError("Error Disabling Service", "An error occurred while trying to disable the data service. Is the monitor service running?"); + } + HideLoad(); + } + + internal void ShowLoad(string title, string msg) { + this.Dispatcher.Invoke(() => { + LoadingDetails.Text = msg; + LoadingTitle.Content = title; + LoadProgress.IsIndeterminate = true; + LoadingScreen.Visibility = Visibility.Visible; + UpdateLayout(); + }); + } + + internal void HideLoad() { + this.Dispatcher.Invoke(() => { + LoadingScreen.Visibility = Visibility.Collapsed; + LoadProgress.IsIndeterminate = false; + }); + } + + private void FormFadeOut_Completed(object sender, EventArgs e) { + closeCompleted = true; + } + private bool closeCompleted = false; + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + if (!closeCompleted) { + FormFadeOut.Begin(); + e.Cancel = true; + } + } + + public void ShowError(string title, string message) { + this.Dispatcher.Invoke(() => { + ErrorTitle.Content = title; + ErrorDetails.Text = message; + ErrorView.Visibility = Visibility.Visible; + }); + } + + private void CloseError(object sender, RoutedEventArgs e) { + this.Dispatcher.Invoke(() => { + ErrorView.Visibility = Visibility.Collapsed; + NoServiceView.Visibility = Visibility.Collapsed; + CloseErrorButton.IsEnabled = true; + }); + } + + private void CloseApp(object sender, RoutedEventArgs e) { + Application.Current.Shutdown(); + } + + private void MainUI_Deactivated(object sender, EventArgs e) { + if (this._isAttached) { +#if DEBUG + logger.Debug("debug is enabled - windows pinned"); +#else + this.Visibility = Visibility.Collapsed; +#endif + } + } + + private void Label_MouseDoubleClick(object sender, MouseButtonEventArgs e) { + Placement(); + } + + int cur = 0; + LogLevelEnum[] levels = new LogLevelEnum[] { LogLevelEnum.FATAL, LogLevelEnum.ERROR, LogLevelEnum.WARN, LogLevelEnum.INFO, LogLevelEnum.DEBUG, LogLevelEnum.TRACE, LogLevelEnum.VERBOSE }; + public LogLevelEnum NextLevel() { + cur++; + if (cur > 6) { + cur = 0; + } + return levels[cur]; + } + + private void IdList_LayoutUpdated(object sender, EventArgs e) { + Placement(); + } + + async private void CollectLogFileClick(object sender, RoutedEventArgs e) { + await CollectLogFiles(); + } + async private Task CollectLogFiles() { + MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); + if (resp != null) { + logger.Info("response: {0}", resp.Message); + } else { + ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback. Is the monitor service running?"); + } + } + + /// + /// Show the blurb as a growler notification + /// + /// The message to show + /// The url or action name to execute + public async Task ShowBlurbAsync(string message, string url, string level = "error") { + RedBlurb.Visibility = Visibility.Collapsed; + InfoBlurb.Visibility = Visibility.Collapsed; + if (level == "error") { + RedBlurb.Visibility = Visibility.Visible; + } else { + InfoBlurb.Visibility = Visibility.Visible; + } + Blurb.Content = message; + _blurbUrl = url; + BlurbArea.Visibility = Visibility.Visible; + BlurbArea.Opacity = 0; + BlurbArea.Margin = new Thickness(0, 0, 0, 0); + DoubleAnimation animation = new DoubleAnimation(1, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(15, 0, 15, 15), TimeSpan.FromSeconds(.3)); + BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); + BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); + await Task.Delay(5000); + HideBlurb(); + } + + /// + /// Execute the hide operation wihout an action from the growler + /// + /// The object that was clicked + /// The click event + private void DoHideBlurb(object sender, MouseButtonEventArgs e) { + HideBlurb(); + } + + /// + /// Hide the blurb area + /// + private void HideBlurb() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + animation.Completed += HideComplete; + BlurbArea.BeginAnimation(Grid.OpacityProperty, animation); + BlurbArea.BeginAnimation(Grid.MarginProperty, animateThick); + } + + /// + /// Hide the blurb area after the animation fades out + /// + /// The animation object + /// The completion event + private void HideComplete(object sender, EventArgs e) { + BlurbArea.Visibility = Visibility.Collapsed; + } + + /// + /// Execute a predefined action or url when the pop up is clicked + /// + /// The object that was clicked + /// The click event + private void BlurbAction(object sender, MouseButtonEventArgs e) { + if (_blurbUrl.Length > 0) { + // So this simply execute a url but you could do like if (_blurbUrl=="DoSomethingNifty") CallNifyFunction(); + if (_blurbUrl == this.RECOVER) { + this.ShowMFA(IdentityMenu.Identity, 4); + } else { + Process.Start(new ProcessStartInfo(_blurbUrl) { UseShellExecute = true }); + } + HideBlurb(); + } else { + HideBlurb(); + } + } + + private void ShowAuthenticate(ZitiIdentity identity) { + MFAAuthenticate(identity); + } + + private void ShowRecovery(ZitiIdentity identity) { + ShowMFARecoveryCodes(identity); + } + + + + + + private ICommand someCommand; + public ICommand SomeCommand { + get { + return someCommand + ?? (someCommand = new ActionCommand(() => { + if (DebugForm.Visibility == Visibility.Hidden) { + DebugForm.Visibility = Visibility.Visible; + } else { + DebugForm.Visibility = Visibility.Hidden; + } + })); + } + set { + someCommand = value; + } + } + + private void DoLoading(bool isComplete) { + if (isComplete) HideLoad(); + else ShowLoad("Loading", "Please Wait."); + } + + private void Label_MouseDoubleClick_1(object sender, MouseButtonEventArgs e) { + ShowToast("here's a toast all rightddd..."); + } + } + + public class ActionCommand : ICommand { + private readonly Action _action; + + public ActionCommand(Action action) { + _action = action; + } + + public void Execute(object parameter) { + _action(); + } + + public bool CanExecute(object parameter) { + return true; + } +#pragma warning disable CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used + public event EventHandler CanExecuteChanged; +#pragma warning restore CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used + } +} diff --git a/DesktopEdge/Models/ZitiIdentity.cs b/DesktopEdge/Models/ZitiIdentity.cs index 09f514c37..7c66dec09 100644 --- a/DesktopEdge/Models/ZitiIdentity.cs +++ b/DesktopEdge/Models/ZitiIdentity.cs @@ -1,174 +1,174 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using NLog; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace ZitiDesktopEdge.Models { - public class ZitiIdentity { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - public List Services { get; set; } - public string Name { get; set; } - public string ControllerUrl { get; set; } - - public string ContollerVersion { get; set; } - public bool IsEnabled { get; set; } - public string EnrollmentStatus { get; set; } - public string Status { get; set; } - public bool IsMFAEnabled { get; set; } - - public void MFADebug(string where) { - logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}\n\tNeedsExtAuth : {NeedsExtAuth}"); - } - - private bool mfaNeeded = false; - public bool IsMFANeeded { - get { return mfaNeeded; } - set { - mfaNeeded = value; - if (!mfaNeeded) { - IsTimingOut = false; - IsTimedOut = false; - WasFullNotified = false; - WasNotified = false; - } - } - } - public int MinTimeout { get; set; } - public int MaxTimeout { get; set; } - public DateTime LastUpdatedTime { get; set; } - public string TimeoutMessage { get; set; } - public bool WasNotified { get; set; } - public bool WasFullNotified { get; set; } - public string Fingerprint { get; set; } - public string Identifier { get; set; } - private bool isTimedOut = false; - - public SemaphoreSlim Mutex { get; } = new SemaphoreSlim(1); - public bool IsTimedOut { - get { return isTimedOut; } - set { - isTimedOut = value; - WasFullNotified = false; - } - } - public string[] RecoveryCodes { get; set; } - public bool IsTimingOut { get; set; } - public bool IsConnected { get; set; } - - - private bool svcFailingPostureCheck = false; - public bool HasServiceFailingPostureCheck { - get { - return svcFailingPostureCheck; - } - set { - logger.Info("Identity: {0} posture change. is a posture check failing: {1}", Name, !value); - svcFailingPostureCheck = value; - if (!value) { - IsMFANeeded = true; - } - } - } - - public bool NeedsExtAuth { get; set; } - - /// - /// Default constructor to support named initialization - /// - public ZitiIdentity() { - this.IsConnected = true; - this.Services = new List(); - } - - public ZitiIdentity(string Name, string ControllerUrl, bool IsEnabled, List Services) { - this.Name = Name; - this.Services = Services; - this.ControllerUrl = ControllerUrl; - this.IsEnabled = IsEnabled; - this.EnrollmentStatus = "Enrolled"; - this.Status = "Available"; - this.MaxTimeout = -1; - this.MinTimeout = -1; - this.LastUpdatedTime = DateTime.Now; - this.TimeoutMessage = ""; - this.RecoveryCodes = new string[0]; - this.IsTimingOut = false; - this.IsTimedOut = false; - this.IsConnected = true; - } - - public static ZitiIdentity FromClient(DataStructures.Identity id) { - ZitiIdentity zid = new ZitiIdentity() { - ControllerUrl = (id.Config == null) ? "" : id.Config.ztAPI, - ContollerVersion = id.ControllerVersion, - EnrollmentStatus = "status", - Fingerprint = id.FingerPrint, - Identifier = id.Identifier, - IsEnabled = id.Active, - Name = (string.IsNullOrEmpty(id.Name) ? id.FingerPrint : id.Name), - Status = id.Status, - RecoveryCodes = new string[0], - IsMFAEnabled = id.MfaEnabled, - IsMFANeeded = id.MfaNeeded, - IsTimedOut = false, - IsTimingOut = false, - MinTimeout = id.MinTimeout, - MaxTimeout = id.MaxTimeout, - LastUpdatedTime = id.MfaLastUpdatedTime, - TimeoutMessage = "", - IsConnected = true, - NeedsExtAuth = id.NeedsExtAuth, - }; - - if (zid.Name.Contains(@"\")) { - int pos = zid.Name.LastIndexOf(@"\"); - zid.Name = zid.Name.Substring(pos + 1); - } - -#if DEBUG - zid.MFADebug("002"); -#endif - if (id.Services != null) { - foreach (var svc in id.Services) { - if (svc != null) { - var zsvc = new ZitiService(svc); - zsvc.TimeUpdated = zid.LastUpdatedTime; - zid.Services.Add(zsvc); - } - } - zid.HasServiceFailingPostureCheck = zid.Services.Any(p => !p.HasFailingPostureCheck()); - } - logger.Info("Identity: {0} updated To {1}", zid.Name, Newtonsoft.Json.JsonConvert.SerializeObject(id)); - return zid; - } - - public void ShowMFAToast(string message) { - logger.Info("Showing Notification from identity " + Name + " " + message + "."); - new Microsoft.Toolkit.Uwp.Notifications.ToastContentBuilder() - .AddText(Name + " Service Access Warning") - .AddText(message) - .AddArgument("identifier", Identifier) - .SetBackgroundActivation() - .Show(); - } - } -} +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ZitiDesktopEdge.Models { + public class ZitiIdentity { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + public List Services { get; set; } + public string Name { get; set; } + public string ControllerUrl { get; set; } + + public string ContollerVersion { get; set; } + public bool IsEnabled { get; set; } + public string EnrollmentStatus { get; set; } + public string Status { get; set; } + public bool IsMFAEnabled { get; set; } + + public void MFADebug(string where) { + logger.Info($"{where}\n\tIdentifiter : {Identifier}\n\tIsMFAEnabled : {IsMFAEnabled}\n\tIsMFANeeded : {IsMFANeeded}\n\tNeedsExtAuth : {NeedsExtAuth}"); + } + + private bool mfaNeeded = false; + public bool IsMFANeeded { + get { return mfaNeeded; } + set { + mfaNeeded = value; + if (!mfaNeeded) { + IsTimingOut = false; + IsTimedOut = false; + WasFullNotified = false; + WasNotified = false; + } + } + } + public int MinTimeout { get; set; } + public int MaxTimeout { get; set; } + public DateTime LastUpdatedTime { get; set; } + public string TimeoutMessage { get; set; } + public bool WasNotified { get; set; } + public bool WasFullNotified { get; set; } + public string Fingerprint { get; set; } + public string Identifier { get; set; } + private bool isTimedOut = false; + + public SemaphoreSlim Mutex { get; } = new SemaphoreSlim(1); + public bool IsTimedOut { + get { return isTimedOut; } + set { + isTimedOut = value; + WasFullNotified = false; + } + } + public string[] RecoveryCodes { get; set; } + public bool IsTimingOut { get; set; } + public bool IsConnected { get; set; } + + + private bool svcFailingPostureCheck = false; + public bool HasServiceFailingPostureCheck { + get { + return svcFailingPostureCheck; + } + set { + logger.Info("Identity: {0} posture change. is a posture check failing: {1}", Name, !value); + svcFailingPostureCheck = value; + if (!value) { + IsMFANeeded = true; + } + } + } + + public bool NeedsExtAuth { get; set; } + + /// + /// Default constructor to support named initialization + /// + public ZitiIdentity() { + this.IsConnected = true; + this.Services = new List(); + } + + public ZitiIdentity(string Name, string ControllerUrl, bool IsEnabled, List Services) { + this.Name = Name; + this.Services = Services; + this.ControllerUrl = ControllerUrl; + this.IsEnabled = IsEnabled; + this.EnrollmentStatus = "Enrolled"; + this.Status = "Available"; + this.MaxTimeout = -1; + this.MinTimeout = -1; + this.LastUpdatedTime = DateTime.Now; + this.TimeoutMessage = ""; + this.RecoveryCodes = new string[0]; + this.IsTimingOut = false; + this.IsTimedOut = false; + this.IsConnected = true; + } + + public static ZitiIdentity FromClient(DataStructures.Identity id) { + ZitiIdentity zid = new ZitiIdentity() { + ControllerUrl = (id.Config == null) ? "" : id.Config.ztAPI, + ContollerVersion = id.ControllerVersion, + EnrollmentStatus = "status", + Fingerprint = id.FingerPrint, + Identifier = id.Identifier, + IsEnabled = id.Active, + Name = (string.IsNullOrEmpty(id.Name) ? id.FingerPrint : id.Name), + Status = id.Status, + RecoveryCodes = new string[0], + IsMFAEnabled = id.MfaEnabled, + IsMFANeeded = id.MfaNeeded, + IsTimedOut = false, + IsTimingOut = false, + MinTimeout = id.MinTimeout, + MaxTimeout = id.MaxTimeout, + LastUpdatedTime = id.MfaLastUpdatedTime, + TimeoutMessage = "", + IsConnected = true, + NeedsExtAuth = id.NeedsExtAuth, + }; + + if (zid.Name.Contains(@"\")) { + int pos = zid.Name.LastIndexOf(@"\"); + zid.Name = zid.Name.Substring(pos + 1); + } + +#if DEBUG + zid.MFADebug("002"); +#endif + if (id.Services != null) { + foreach (var svc in id.Services) { + if (svc != null) { + var zsvc = new ZitiService(svc); + zsvc.TimeUpdated = zid.LastUpdatedTime; + zid.Services.Add(zsvc); + } + } + zid.HasServiceFailingPostureCheck = zid.Services.Any(p => !p.HasFailingPostureCheck()); + } + logger.Info("Identity: {0} updated To {1}", zid.Name, Newtonsoft.Json.JsonConvert.SerializeObject(id)); + return zid; + } + + public void ShowMFAToast(string message) { + logger.Info("Showing Notification from identity " + Name + " " + message + "."); + new Microsoft.Toolkit.Uwp.Notifications.ToastContentBuilder() + .AddText(Name + " Service Access Warning") + .AddText(message) + .AddArgument("identifier", Identifier) + .SetBackgroundActivation() + .Show(); + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs index af0e3e06b..7cd8d70dd 100644 --- a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs @@ -1,387 +1,387 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using ZitiDesktopEdge.Models; -using ZitiDesktopEdge.ServiceClient; -using NLog; -using SWM = System.Windows.Media; -using ZitiDesktopEdge.DataStructures; -using System.Diagnostics; -using System.Web.UI; - -namespace ZitiDesktopEdge { - /// - /// User Control to list Identities and give status - /// - public partial class IdentityItem : System.Windows.Controls.UserControl { - - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - public delegate void StatusChanged(bool attached); - public event StatusChanged OnStatusChanged; - public delegate void OnAuthenticate(ZitiIdentity identity); - public event OnAuthenticate Authenticate; - public delegate void OnIdentityChanged(ZitiIdentity identity); - public event OnIdentityChanged IdentityChanged; - private System.Windows.Forms.Timer _timer; - private System.Windows.Forms.Timer _timingTimer; - private float countdown = -1; - private float countdownComplete = -1; - private int available = 0; - - private static SWM.Color mfaOrange = SWM.Color.FromRgb(0xA1, 0x8B, 0x10); - private static SWM.Color defaultBlue = SWM.Color.FromRgb(0x00, 0x68, 0xF9); - private static SWM.Brush MFANeededBrush = new SWM.SolidColorBrush(mfaOrange); - private static SWM.Brush DefaultBrush = new SWM.SolidColorBrush(defaultBlue); - - public ZitiIdentity _identity; - public ZitiIdentity Identity { - get { - return _identity; - } - set { - _identity = value; - this.RefreshUI(); - } - } - - /// - /// Object constructor, setup the events for the control - /// - public IdentityItem() { - InitializeComponent(); - ToggleSwitch.OnToggled += ToggleIdentity; - } - - public void StopTimers() { - _timer?.Stop(); - _timingTimer?.Stop(); - } - - public int GetMaxTimeout() { - int maxto = -1; - for (int i = 0; i < _identity.Services.Count; i++) { - ZitiService info = _identity.Services[i]; - - if (info.TimeoutCalculated > -1) { - if (info.TimeoutCalculated == 0) { - available--; - } - if (info.TimeoutCalculated > maxto) maxto = info.TimeoutCalculated; - - } - logger.Trace("Max: " + _identity.Name + " " + maxto + " " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated + " " + DateTime.Now); - } - return maxto; - } - public int GetMinTimeout() { - int minto = int.MaxValue; - for (int i = 0; i < _identity.Services.Count; i++) { - ZitiService info = _identity.Services[i]; - if (info.TimeoutCalculated > -1) { - if (info.TimeoutCalculated < minto) minto = info.TimeoutCalculated; - } - // logger.Trace("Min: " + _identity.Name + " " + minto + " " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated+" "+ DateTime.Now); - } - if (minto == int.MaxValue) minto = 0; - return minto; - } - - private void MFAEnabledAndNeeded() { - MfaRequired.Visibility = Visibility.Visible; - ServiceCountArea.Visibility = Visibility.Collapsed; - MainArea.Opacity = 0.6; - ServiceCountAreaLabel.Content = "authorize"; - - float maxto = GetMaxTimeout(); - if (maxto > -1) { - if (maxto > 0) { - if (_timer != null) _timer.Stop(); - countdownComplete = maxto; - _timer = new System.Windows.Forms.Timer(); - _timer.Interval = 1000; - _timer.Tick += TimerTicked; - _timer.Start(); - logger.Info("Timer Started for full timout in " + maxto + " seconds from identity " + _identity.Name + "."); - } else { - //if (maxto == 0) ShowTimedOut(); - } - } - float minto = GetMinTimeout(); - logger.Info("Min/Max For " + _identity.Name + " " + minto + " " + maxto); - if (minto > -1) { - if (minto > 0) { - if (_timingTimer != null) _timingTimer.Stop(); - countdown = minto; - _timingTimer = new System.Windows.Forms.Timer(); - _timingTimer.Interval = 1000; - _timingTimer.Tick += TimingTimerTick; - _timingTimer.Start(); - logger.Info("Timer Started for first timout in " + minto + " seconds from identity " + _identity.Name + " value with " + _identity.MinTimeout + "."); - } else { - if (maxto > 0) { - ShowTimeout(); - } - } - } - logger.Info("RefreshUI " + _identity.Name + " Min: " + minto + " Max: " + maxto); - } - - private void MFAEnabledAndNotNeeded() { - if (_identity.IsTimedOut) { - PostureTimedOut.Visibility = Visibility.Visible; - ServiceCountAreaLabel.Content = "authorize2"; - MainArea.Opacity = 1.0; - } else { - //MfaRequired.Visibility = Visibility.Visible; - //ServiceCountAreaLabel.Content = "authenticate1"; - //MainArea.Opacity = 0.6; - MfaRequired.Visibility = Visibility.Collapsed; - } - ServiceCountBorder.Background = DefaultBrush; - } - - private void MFANotEnabledAndNotNeeded() { - ServiceCountAreaLabel.Content = "services"; - MainArea.Opacity = 1.0; - } - - private void MFANotEnabledAndNeeded() { - ServiceCount.Content = "MFA"; - ServiceCountBorder.Background = MFANeededBrush; - ServiceCountAreaLabel.Content = "disabled"; - } - - public void RefreshUI() { - if (_identity.IsConnected) { - this.IsEnabled = true; - this.Opacity = 1.0; - } else { - this.IsEnabled = false; - this.Opacity = 0.3; - } - TimerCountdown.Visibility = Visibility.Collapsed; - PostureTimedOut.Visibility = Visibility.Collapsed; - MfaRequired.Visibility = Visibility.Collapsed; - available = _identity.Services.Count; - ToggleSwitch.Enabled = _identity.IsEnabled; - ServiceCountAreaLabel.Content = "services"; - ServiceCount.Content = _identity.Services.Count.ToString(); - MainArea.Opacity = 1.0; - ServiceCountArea.Visibility = Visibility.Visible; - ServiceCountAreaLabel.Content = "services"; - // logger.Info("RefreshUI " + _identity.Name + " MFA: "+ _identity.IsMFAEnabled+" Authenticated: "+_identity.IsAuthenticated); - - ServiceCount.Content = _identity.Services.Count.ToString(); - if (_identity.IsMFAEnabled) { - if (_identity.IsMFANeeded) { - // enabled and needed = needs to be authorized. show the lock icon and tell the user to auth - MFAEnabledAndNeeded(); - } else { - // enabled and not needed = authorized. show the services should be enabled and authorized - MFAEnabledAndNotNeeded(); - } - } else { - if (_identity.IsMFANeeded) { - // not enabled and needed = show the user the MFA disabled so they can enable it - MFANotEnabledAndNeeded(); - } else { - // normal case. means no lock icon needs to be shown - MFANotEnabledAndNotNeeded(); - } - } - - int idViewState = CalculateIdentityState(_identity); - - if (idViewState == 0) { - ExtAuthRequired.Visibility = Visibility.Collapsed; - MfaRequired.Visibility = Visibility.Collapsed; - ServiceCountArea.Visibility = Visibility.Visible; - } else if (idViewState % (int)IdentityStates.NeedsExtAuth == 0) { - ExtAuthRequired.Visibility = Visibility.Visible; - MfaRequired.Visibility = Visibility.Collapsed; - ServiceCountArea.Visibility = Visibility.Collapsed; - } else if (idViewState % (int)IdentityStates.NeedsMfa == 0) { - ExtAuthRequired.Visibility = Visibility.Collapsed; - MfaRequired.Visibility = Visibility.Visible; - ServiceCountArea.Visibility = Visibility.Collapsed; - } - - IdName.Content = _identity.Name; - IdUrl.Content = _identity.ControllerUrl; - if (_identity.ContollerVersion != null && _identity.ContollerVersion.Length > 0) IdUrl.Content = _identity.ControllerUrl + " at " + _identity.ContollerVersion; - - ToggleStatus.Content = ((ToggleSwitch.Enabled) ? "ENABLED" : "DISABLED"); - } - - private int CalculateIdentityState(ZitiIdentity id) { - int ret = 0; - if (id.NeedsExtAuth) { - ret += (int)IdentityStates.NeedsExtAuth; - } - if (id.IsMFANeeded) { - ret += (int)IdentityStates.NeedsMfa; - } - return ret; - } - - enum IdentityStates { - NeedsMfa = 1, - NeedsExtAuth = 2, - } - - private void TimingTimerTick(object sender, EventArgs e) { - available = _identity.Services.Count; - GetMaxTimeout(); - TimerCountdown.Visibility = Visibility.Collapsed; - if (countdown > -1) { - countdown--; - logger.Trace("CountDown " + countdown + " seconds from identity " + _identity.Name + "."); - if (countdown > 0) { - TimeSpan t = TimeSpan.FromSeconds(countdown); - string answer = t.Seconds + " seconds"; - if (t.Days > 0) answer = t.Days + " days " + t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; - else { - if (t.Hours > 0) answer = t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; - else { - if (t.Minutes > 0) answer = t.Minutes + " minutes " + t.Seconds + " seconds"; - } - } - if (countdown <= 1200) { - ShowTimeout(); - - if (!_identity.WasNotified) { - _identity.WasNotified = true; - _identity.ShowMFAToast("The services for " + _identity.Name + " will start to time out in " + answer); - } - } - - if (available < _identity.Services.Count) MainArea.ToolTip = (_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."; - else MainArea.ToolTip = "Some or all of the services will be timing out in " + answer; - } else { - ShowTimeout(); - MainArea.ToolTip = (_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."; - ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; - } - } else { - ShowTimeout(); - MainArea.ToolTip = "Some or all of the services have timed out."; - ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; - } - } - - private void ShowTimeout() { - TimerCountdown.Visibility = Visibility.Visible; - ServiceCountArea.Visibility = Visibility.Collapsed; - MfaRequired.Visibility = Visibility.Collapsed; - ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; - if (!_identity.WasNotified) { - if (available < _identity.Services.Count) { - _identity.WasNotified = true; - _identity.ShowMFAToast((_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."); - } - _identity.IsTimingOut = true; - - this.IdentityChanged?.Invoke(_identity); - } - } - - private void ShowTimedOut() { - _identity.Mutex.Wait(); - Console.WriteLine(_identity.Mutex.GetHashCode()); - if (!_identity.WasFullNotified) { - _identity.WasFullNotified = true; - _identity.ShowMFAToast("All of the services with a timeout set for the identity " + _identity.Name + " have timed out"); - RefreshUI(); - if (_timer != null) _timer.Stop(); - } - _identity.Mutex.Release(); - } - - private void TimerTicked(object sender, EventArgs e) { - if (countdownComplete > -1) { - countdownComplete--; - if (countdownComplete <= 0) ShowTimedOut(); - } - } - - async private void ToggleIdentity(bool on) { - try { - if (OnStatusChanged != null) { - OnStatusChanged(on); - } - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - DataStructures.Identity id = await client.IdentityOnOffAsync(_identity.Identifier, on); - this.Identity.IsEnabled = on; - if (on) { - ToggleStatus.Content = "ENABLED"; - } else { - ToggleStatus.Content = "DISABLED"; - } - } catch (DataStructures.ServiceException se) { - MessageBox.Show(se.AdditionalInfo, se.Message); - } catch (Exception ex) { - MessageBox.Show("Error", ex.Message); - } - } - - private void Canvas_MouseEnter(object sender, MouseEventArgs e) { - OverState.Opacity = 0.2; - } - - private void Canvas_MouseLeave(object sender, MouseEventArgs e) { - OverState.Opacity = 0; - } - - private void OpenDetails(object sender, MouseButtonEventArgs e) { - IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; - deets.SelectedIdentity = this; - deets.Identity = this.Identity; - } - - private void MFAAuthenticate(object sender, MouseButtonEventArgs e) { - this.Authenticate?.Invoke(_identity); - } - - private void ToggledSwitch(object sender, MouseButtonEventArgs e) { - ToggleSwitch.Toggle(); - } - - private void DoMFAOrOpen(object sender, MouseButtonEventArgs e) { - if (MfaRequired.Visibility == Visibility.Visible || TimerCountdown.Visibility == Visibility.Visible || PostureTimedOut.Visibility == Visibility.Visible) { - MFAAuthenticate(sender, e); - } else if (ExtAuthRequired.Visibility == Visibility.Visible) { - CompleteExtAuth(sender, e); - } else { - OpenDetails(sender, e); - } - } - - async private void CompleteExtAuth(object sender, MouseButtonEventArgs e) { - try { - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - ExternalAuthLoginResponse resp = await client.ExternalAuthLogin(_identity.Identifier); - Console.WriteLine(resp.Data?.url); - Process.Start(resp.Data.url); - } catch (Exception ex) { - logger.Error("unexpected error!", ex); - } - } - } -} +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using ZitiDesktopEdge.Models; +using ZitiDesktopEdge.ServiceClient; +using NLog; +using SWM = System.Windows.Media; +using ZitiDesktopEdge.DataStructures; +using System.Diagnostics; +using System.Web.UI; + +namespace ZitiDesktopEdge { + /// + /// User Control to list Identities and give status + /// + public partial class IdentityItem : System.Windows.Controls.UserControl { + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + public delegate void StatusChanged(bool attached); + public event StatusChanged OnStatusChanged; + public delegate void OnAuthenticate(ZitiIdentity identity); + public event OnAuthenticate Authenticate; + public delegate void OnIdentityChanged(ZitiIdentity identity); + public event OnIdentityChanged IdentityChanged; + private System.Windows.Forms.Timer _timer; + private System.Windows.Forms.Timer _timingTimer; + private float countdown = -1; + private float countdownComplete = -1; + private int available = 0; + + private static SWM.Color mfaOrange = SWM.Color.FromRgb(0xA1, 0x8B, 0x10); + private static SWM.Color defaultBlue = SWM.Color.FromRgb(0x00, 0x68, 0xF9); + private static SWM.Brush MFANeededBrush = new SWM.SolidColorBrush(mfaOrange); + private static SWM.Brush DefaultBrush = new SWM.SolidColorBrush(defaultBlue); + + public ZitiIdentity _identity; + public ZitiIdentity Identity { + get { + return _identity; + } + set { + _identity = value; + this.RefreshUI(); + } + } + + /// + /// Object constructor, setup the events for the control + /// + public IdentityItem() { + InitializeComponent(); + ToggleSwitch.OnToggled += ToggleIdentity; + } + + public void StopTimers() { + _timer?.Stop(); + _timingTimer?.Stop(); + } + + public int GetMaxTimeout() { + int maxto = -1; + for (int i = 0; i < _identity.Services.Count; i++) { + ZitiService info = _identity.Services[i]; + + if (info.TimeoutCalculated > -1) { + if (info.TimeoutCalculated == 0) { + available--; + } + if (info.TimeoutCalculated > maxto) maxto = info.TimeoutCalculated; + + } + logger.Trace("Max: " + _identity.Name + " " + maxto + " " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated + " " + DateTime.Now); + } + return maxto; + } + public int GetMinTimeout() { + int minto = int.MaxValue; + for (int i = 0; i < _identity.Services.Count; i++) { + ZitiService info = _identity.Services[i]; + if (info.TimeoutCalculated > -1) { + if (info.TimeoutCalculated < minto) minto = info.TimeoutCalculated; + } + // logger.Trace("Min: " + _identity.Name + " " + minto + " " + info.Name + " " + info.Timeout + " " + info.TimeoutCalculated + " " + info.TimeoutRemaining + " " + info.TimeUpdated+" "+ DateTime.Now); + } + if (minto == int.MaxValue) minto = 0; + return minto; + } + + private void MFAEnabledAndNeeded() { + MfaRequired.Visibility = Visibility.Visible; + ServiceCountArea.Visibility = Visibility.Collapsed; + MainArea.Opacity = 0.6; + ServiceCountAreaLabel.Content = "authorize"; + + float maxto = GetMaxTimeout(); + if (maxto > -1) { + if (maxto > 0) { + if (_timer != null) _timer.Stop(); + countdownComplete = maxto; + _timer = new System.Windows.Forms.Timer(); + _timer.Interval = 1000; + _timer.Tick += TimerTicked; + _timer.Start(); + logger.Info("Timer Started for full timout in " + maxto + " seconds from identity " + _identity.Name + "."); + } else { + //if (maxto == 0) ShowTimedOut(); + } + } + float minto = GetMinTimeout(); + logger.Info("Min/Max For " + _identity.Name + " " + minto + " " + maxto); + if (minto > -1) { + if (minto > 0) { + if (_timingTimer != null) _timingTimer.Stop(); + countdown = minto; + _timingTimer = new System.Windows.Forms.Timer(); + _timingTimer.Interval = 1000; + _timingTimer.Tick += TimingTimerTick; + _timingTimer.Start(); + logger.Info("Timer Started for first timout in " + minto + " seconds from identity " + _identity.Name + " value with " + _identity.MinTimeout + "."); + } else { + if (maxto > 0) { + ShowTimeout(); + } + } + } + logger.Info("RefreshUI " + _identity.Name + " Min: " + minto + " Max: " + maxto); + } + + private void MFAEnabledAndNotNeeded() { + if (_identity.IsTimedOut) { + PostureTimedOut.Visibility = Visibility.Visible; + ServiceCountAreaLabel.Content = "authorize2"; + MainArea.Opacity = 1.0; + } else { + //MfaRequired.Visibility = Visibility.Visible; + //ServiceCountAreaLabel.Content = "authenticate1"; + //MainArea.Opacity = 0.6; + MfaRequired.Visibility = Visibility.Collapsed; + } + ServiceCountBorder.Background = DefaultBrush; + } + + private void MFANotEnabledAndNotNeeded() { + ServiceCountAreaLabel.Content = "services"; + MainArea.Opacity = 1.0; + } + + private void MFANotEnabledAndNeeded() { + ServiceCount.Content = "MFA"; + ServiceCountBorder.Background = MFANeededBrush; + ServiceCountAreaLabel.Content = "disabled"; + } + + public void RefreshUI() { + if (_identity.IsConnected) { + this.IsEnabled = true; + this.Opacity = 1.0; + } else { + this.IsEnabled = false; + this.Opacity = 0.3; + } + TimerCountdown.Visibility = Visibility.Collapsed; + PostureTimedOut.Visibility = Visibility.Collapsed; + MfaRequired.Visibility = Visibility.Collapsed; + available = _identity.Services.Count; + ToggleSwitch.Enabled = _identity.IsEnabled; + ServiceCountAreaLabel.Content = "services"; + ServiceCount.Content = _identity.Services.Count.ToString(); + MainArea.Opacity = 1.0; + ServiceCountArea.Visibility = Visibility.Visible; + ServiceCountAreaLabel.Content = "services"; + // logger.Info("RefreshUI " + _identity.Name + " MFA: "+ _identity.IsMFAEnabled+" Authenticated: "+_identity.IsAuthenticated); + + ServiceCount.Content = _identity.Services.Count.ToString(); + if (_identity.IsMFAEnabled) { + if (_identity.IsMFANeeded) { + // enabled and needed = needs to be authorized. show the lock icon and tell the user to auth + MFAEnabledAndNeeded(); + } else { + // enabled and not needed = authorized. show the services should be enabled and authorized + MFAEnabledAndNotNeeded(); + } + } else { + if (_identity.IsMFANeeded) { + // not enabled and needed = show the user the MFA disabled so they can enable it + MFANotEnabledAndNeeded(); + } else { + // normal case. means no lock icon needs to be shown + MFANotEnabledAndNotNeeded(); + } + } + + int idViewState = CalculateIdentityState(_identity); + + if (idViewState == 0) { + ExtAuthRequired.Visibility = Visibility.Collapsed; + MfaRequired.Visibility = Visibility.Collapsed; + ServiceCountArea.Visibility = Visibility.Visible; + } else if (idViewState % (int)IdentityStates.NeedsExtAuth == 0) { + ExtAuthRequired.Visibility = Visibility.Visible; + MfaRequired.Visibility = Visibility.Collapsed; + ServiceCountArea.Visibility = Visibility.Collapsed; + } else if (idViewState % (int)IdentityStates.NeedsMfa == 0) { + ExtAuthRequired.Visibility = Visibility.Collapsed; + MfaRequired.Visibility = Visibility.Visible; + ServiceCountArea.Visibility = Visibility.Collapsed; + } + + IdName.Content = _identity.Name; + IdUrl.Content = _identity.ControllerUrl; + if (_identity.ContollerVersion != null && _identity.ContollerVersion.Length > 0) IdUrl.Content = _identity.ControllerUrl + " at " + _identity.ContollerVersion; + + ToggleStatus.Content = ((ToggleSwitch.Enabled) ? "ENABLED" : "DISABLED"); + } + + private int CalculateIdentityState(ZitiIdentity id) { + int ret = 0; + if (id.NeedsExtAuth) { + ret += (int)IdentityStates.NeedsExtAuth; + } + if (id.IsMFANeeded) { + ret += (int)IdentityStates.NeedsMfa; + } + return ret; + } + + enum IdentityStates { + NeedsMfa = 1, + NeedsExtAuth = 2, + } + + private void TimingTimerTick(object sender, EventArgs e) { + available = _identity.Services.Count; + GetMaxTimeout(); + TimerCountdown.Visibility = Visibility.Collapsed; + if (countdown > -1) { + countdown--; + logger.Trace("CountDown " + countdown + " seconds from identity " + _identity.Name + "."); + if (countdown > 0) { + TimeSpan t = TimeSpan.FromSeconds(countdown); + string answer = t.Seconds + " seconds"; + if (t.Days > 0) answer = t.Days + " days " + t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; + else { + if (t.Hours > 0) answer = t.Hours + " hours " + t.Minutes + " minutes " + t.Seconds + " seconds"; + else { + if (t.Minutes > 0) answer = t.Minutes + " minutes " + t.Seconds + " seconds"; + } + } + if (countdown <= 1200) { + ShowTimeout(); + + if (!_identity.WasNotified) { + _identity.WasNotified = true; + _identity.ShowMFAToast("The services for " + _identity.Name + " will start to time out in " + answer); + } + } + + if (available < _identity.Services.Count) MainArea.ToolTip = (_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."; + else MainArea.ToolTip = "Some or all of the services will be timing out in " + answer; + } else { + ShowTimeout(); + MainArea.ToolTip = (_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."; + ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; + } + } else { + ShowTimeout(); + MainArea.ToolTip = "Some or all of the services have timed out."; + ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; + } + } + + private void ShowTimeout() { + TimerCountdown.Visibility = Visibility.Visible; + ServiceCountArea.Visibility = Visibility.Collapsed; + MfaRequired.Visibility = Visibility.Collapsed; + ServiceCountAreaLabel.Content = available + "/" + _identity.Services.Count; + if (!_identity.WasNotified) { + if (available < _identity.Services.Count) { + _identity.WasNotified = true; + _identity.ShowMFAToast((_identity.Services.Count - available) + " of " + _identity.Services.Count + " services have timed out."); + } + _identity.IsTimingOut = true; + + this.IdentityChanged?.Invoke(_identity); + } + } + + private void ShowTimedOut() { + _identity.Mutex.Wait(); + Console.WriteLine(_identity.Mutex.GetHashCode()); + if (!_identity.WasFullNotified) { + _identity.WasFullNotified = true; + _identity.ShowMFAToast("All of the services with a timeout set for the identity " + _identity.Name + " have timed out"); + RefreshUI(); + if (_timer != null) _timer.Stop(); + } + _identity.Mutex.Release(); + } + + private void TimerTicked(object sender, EventArgs e) { + if (countdownComplete > -1) { + countdownComplete--; + if (countdownComplete <= 0) ShowTimedOut(); + } + } + + async private void ToggleIdentity(bool on) { + try { + if (OnStatusChanged != null) { + OnStatusChanged(on); + } + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + DataStructures.Identity id = await client.IdentityOnOffAsync(_identity.Identifier, on); + this.Identity.IsEnabled = on; + if (on) { + ToggleStatus.Content = "ENABLED"; + } else { + ToggleStatus.Content = "DISABLED"; + } + } catch (DataStructures.ServiceException se) { + MessageBox.Show(se.AdditionalInfo, se.Message); + } catch (Exception ex) { + MessageBox.Show("Error", ex.Message); + } + } + + private void Canvas_MouseEnter(object sender, MouseEventArgs e) { + OverState.Opacity = 0.2; + } + + private void Canvas_MouseLeave(object sender, MouseEventArgs e) { + OverState.Opacity = 0; + } + + private void OpenDetails(object sender, MouseButtonEventArgs e) { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + deets.SelectedIdentity = this; + deets.Identity = this.Identity; + } + + private void MFAAuthenticate(object sender, MouseButtonEventArgs e) { + this.Authenticate?.Invoke(_identity); + } + + private void ToggledSwitch(object sender, MouseButtonEventArgs e) { + ToggleSwitch.Toggle(); + } + + private void DoMFAOrOpen(object sender, MouseButtonEventArgs e) { + if (MfaRequired.Visibility == Visibility.Visible || TimerCountdown.Visibility == Visibility.Visible || PostureTimedOut.Visibility == Visibility.Visible) { + MFAAuthenticate(sender, e); + } else if (ExtAuthRequired.Visibility == Visibility.Visible) { + CompleteExtAuth(sender, e); + } else { + OpenDetails(sender, e); + } + } + + async private void CompleteExtAuth(object sender, MouseButtonEventArgs e) { + try { + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + ExternalAuthLoginResponse resp = await client.ExternalAuthLogin(_identity.Identifier); + Console.WriteLine(resp.Data?.url); + Process.Start(resp.Data.url); + } catch (Exception ex) { + logger.Error("unexpected error!", ex); + } + } + } +} diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index 2ce821d3b..2c7835d7f 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -1,1083 +1,1083 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; -using System.Diagnostics; -using System.Linq; -using System.ServiceProcess; -using System.IO; -using System.Timers; -using System.Configuration; -using System.Threading.Tasks; -using System.Threading; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.IO.Compression; - -using ZitiDesktopEdge.DataStructures; -using ZitiDesktopEdge.ServiceClient; -using ZitiDesktopEdge.Server; -using ZitiDesktopEdge.Utility; - -using NLog; -using Newtonsoft.Json; -using System.Net; -using DnsClient; -using DnsClient.Protocol; -using ZitiUpdateService.Utils; -using ZitiUpdateService.Checkers; -using System.Security.Policy; -using Newtonsoft.Json.Linq; -using System.Runtime.Remoting.Messaging; - -#if !SKIPUPDATE -using ZitiUpdateService.Checkers.PeFile; -#endif - -namespace ZitiUpdateService { - public partial class UpdateService : ServiceBase { - private const string betaStreamMarkerFile = "use-beta-stream.txt"; - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private static Settings CurrentSettings = new Settings(true); - - public bool IsBeta { - get { - return File.Exists(Path.Combine(exeLocation, betaStreamMarkerFile)); - } - private set { } - } - - - private System.Timers.Timer _updateTimer = new System.Timers.Timer(); - private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); - - private string exeLocation = null; - - private DataClient dataClient = new DataClient("monitor service"); - private bool running = false; - private string asmDir = null; - private string updateFolder = null; - private string filePrefix = "Ziti.Desktop.Edge.Client-"; - private Version assemblyVersion = null; - - private ServiceController controller; - private IPCServer svr = new IPCServer(); - private Task ipcServer = null; - private Task eventServer = null; - - private const int zetHealthcheckInterval = 5; - private SemaphoreSlim zetSemaphore = new SemaphoreSlim(1, 1); - private System.Timers.Timer zetHealthcheck = new System.Timers.Timer(); - private int zetFailedCheckCounter = 0; - - private UpdateCheck lastUpdateCheck; - private InstallationNotificationEvent lastInstallationNotification; - - public UpdateService() { - InitializeComponent(); - - CurrentSettings.Load(); - CurrentSettings.OnConfigurationChange += CurrentSettings_OnConfigurationChange; - - base.CanHandlePowerEvent = true; - - exeLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - - Logger.Info("Initializing"); - dataClient.OnClientConnected += Svc_OnClientConnected; - dataClient.OnTunnelStatusEvent += Svc_OnTunnelStatusEvent; - dataClient.OnClientDisconnected += Svc_OnClientDisconnected; - dataClient.OnShutdownEvent += Svc_OnShutdownEvent; - dataClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; - dataClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; - - svr.CaptureLogs = CaptureLogs; - svr.SetLogLevel = SetLogLevel; - svr.SetReleaseStream = SetReleaseStream; - svr.DoUpdateCheck = DoUpdateCheck; - svr.TriggerUpdate = TriggerUpdate; - svr.SetAutomaticUpdateDisabled = SetAutomaticUpdateDisabled; - svr.SetAutomaticUpdateURL = SetAutomaticUpdateURL; - - string assemblyVersionStr = Assembly.GetExecutingAssembly().GetName().Version.ToString(); //fetch from ziti? - assemblyVersion = new Version(assemblyVersionStr); - asmDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - updateFolder = Path.Combine(asmDir, "updates"); - if (!Directory.Exists(updateFolder)) { - Directory.CreateDirectory(updateFolder); - } - } - - private SvcResponse SetAutomaticUpdateURL(string url) { - SvcResponse failure = new SvcResponse(); - failure.Code = (int)ErrorCodes.URL_INVALID; - failure.Error = $"The url supplied is invalid: \n{url}\n"; - failure.Message = "Failure"; - - - SvcResponse r = new SvcResponse(); - if (url == null || !url.StartsWith("http")) { - return failure; - } else { - // check the url exists and appears correct... - var check = new GithubCheck(assemblyVersion, url); - - if (check != null) { - var v = check.GetNextVersion(); - - if (v == null) { - return failure; - } - if (v.Revision.ToString().Trim() == "") { - return failure; - } - } - - checkUpdateImmediately(); - - CurrentSettings.AutomaticUpdateURL = url; - CurrentSettings.Write(); - r.Message = "Success"; - } - return r; - } - - private void CurrentSettings_OnConfigurationChange(object sender, ControllerEvent e) { - MonitorServiceStatusEvent evt; - if (lastInstallationNotification != null) { - evt = lastInstallationNotification; - } else { - evt = new MonitorServiceStatusEvent() { - Code = 0, - Error = "", - Message = "Configuration Changed", - Type = "Status", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - }; - } - Logger.Debug($"notifying consumers of change to CurrentSettings. AutomaticUpdates status = {(CurrentSettings.AutomaticUpdatesDisabled ? "disabled" : "enabled")}"); - EventRegistry.SendEventToConsumers(evt); - } - - private SvcResponse SetAutomaticUpdateDisabled(bool disabled) { - if (lastInstallationNotification != null) { - lastInstallationNotification.AutomaticUpgradeDisabled = disabled.ToString(); - } - CurrentSettings.AutomaticUpdatesDisabled = disabled; - CurrentSettings.Write(); - SvcResponse r = new SvcResponse(); - r.Message = "Success"; - return r; - } - - private SvcResponse TriggerUpdate() { - SvcResponse r = new SvcResponse(); - r.Message = "Initiating Update"; - - Task.Run(() => { installZDE(lastUpdateCheck); }); - return r; - } - - - private void checkUpdateImmediately() { - try { - CheckUpdate(null, null); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in CheckUpdate"); - } - } - - private StatusCheck DoUpdateCheck() { - StatusCheck r = new StatusCheck(); - - UpdateCheck check = getCheck(assemblyVersion); - - r.Code = check.Avail; - r.ReleaseStream = IsBeta ? "beta" : "stable"; - switch (r.Code) { - case -1: - r.Message = $"An update is available: {check.GetNextVersion()}"; - r.UpdateAvailable = true; - Logger.Debug("Update {0} is published on {1}", check.GetNextVersion(), check.PublishDate); - checkUpdateImmediately(); - break; - case 0: - r.Message = $"The current version [{assemblyVersion}] is the latest"; - break; - case 1: - r.Message = $"Your version [{assemblyVersion}] is newer than the latest release"; - break; - default: - r.Message = "Update check failed"; - break; - } - return r; - } - - private void SetLogLevel(string level) { - try { - Logger.Info("request to change log level received: {0}", level); - if (("" + level).ToLower().Trim() == "verbose") { - level = "trace"; - Logger.Info("request to change log level to verbose - but using trace instead"); - } - var l = LogLevel.FromString(level); - foreach (var rule in LogManager.Configuration.LoggingRules) { - rule.EnableLoggingForLevel(l); - rule.SetLoggingLevels(l, LogLevel.Fatal); - } - - LogManager.ReconfigExistingLoggers(); - Logger.Info("logger reconfigured to log at level: {0}", l); - } catch (Exception e) { - Logger.Error(e, "Could NOT set the log level for loggers??? {0}", e.Message); - } - } - - private void SetReleaseStream(string stream) { - string markerFile = Path.Combine(exeLocation, betaStreamMarkerFile); - if (stream == "beta") { - if (IsBeta) { - Logger.Debug("already using beta stream. No action taken"); - } else { - Logger.Info("Setting update service to use beta stream!"); - using (File.Create(markerFile)) { - - } - AccessUtils.GrantAccessToFile(markerFile); //allow anyone to delete this manually if need be... - Logger.Debug("added marker file: {0}", markerFile); - } - } else { - if (!IsBeta) { - Logger.Debug("already using release stream. No action taken"); - } else { - Logger.Info("Setting update service to use release stream!"); - if (File.Exists(markerFile)) { - File.Delete(markerFile); - Logger.Debug("removed marker file: {0}", markerFile); - } - } - } - } - - private string CaptureLogs() { - try { - string logLocation = Path.Combine(exeLocation, "logs"); - string destinationLocation = Path.Combine(exeLocation, "temp"); - string serviceLogsLocation = Path.Combine(logLocation, "service"); - string serviceLogsDest = Path.Combine(destinationLocation, "service"); - - Logger.Debug("removing leftover temp folder: {0}", destinationLocation); - try { - Directory.Delete(destinationLocation, true); - } catch { - //means it doesn't exist - } - - Directory.CreateDirectory(destinationLocation); - - Logger.Debug("copying all directories from: {0}", logLocation); - foreach (string dirPath in Directory.GetDirectories(logLocation, "*", SearchOption.AllDirectories)) { - Directory.CreateDirectory(dirPath.Replace(logLocation, destinationLocation)); - } - - Logger.Debug("copying all non-zip files from: {0}", logLocation); - foreach (string newPath in Directory.GetFiles(logLocation, "*.*", SearchOption.AllDirectories)) { - if (!newPath.EndsWith(".zip")) { - File.Copy(newPath, newPath.Replace(logLocation, destinationLocation), true); - } - } - - Logger.Debug("copying service files from: {0} to {1}", serviceLogsLocation, serviceLogsDest); - Directory.CreateDirectory(serviceLogsDest); - foreach (string newPath in Directory.GetFiles(serviceLogsLocation, "*.*", SearchOption.TopDirectoryOnly)) { - if (newPath.EndsWith(".log") || newPath.Contains("config.json")) { - Logger.Debug("copying service log: {0}", newPath); - File.Copy(newPath, newPath.Replace(serviceLogsLocation, serviceLogsDest), true); - } - } - - outputIpconfigInfo(destinationLocation); - outputSystemInfo(destinationLocation); - outputDnsCache(destinationLocation); - outputExternalIP(destinationLocation); - outputTasklist(destinationLocation); - outputRouteInfo(destinationLocation); - outputNetstatInfo(destinationLocation); - outputNrpt(destinationLocation); - - Task.Delay(500).Wait(); - - string zipName = Path.Combine(logLocation, DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".zip"); - ZipFile.CreateFromDirectory(destinationLocation, zipName); - - Logger.Debug("cleaning up temp folder: {0}", destinationLocation); - try { - Directory.Delete(destinationLocation, true); - } catch { - //means it doesn't exist - } - return zipName; - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in generating system files {0}", ex.Message); - return null; - } - } - - private void outputIpconfigInfo(string destinationFolder) { - Logger.Info("capturing ipconfig information"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var ipconfigOut = Path.Combine(destinationFolder, "ipconfig.all.txt"); - Logger.Debug("copying ipconfig /all to {0}", ipconfigOut); - startInfo.Arguments = $"/C ipconfig /all > \"{ipconfigOut}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputIpconfigInfo {0}", ex.Message); - } - } - - private void outputSystemInfo(string destinationFolder) { - Logger.Info("capturing systeminfo"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var sysinfoOut = Path.Combine(destinationFolder, "systeminfo.txt"); - Logger.Debug("running systeminfo to {0}", sysinfoOut); - startInfo.Arguments = $"/C systeminfo > \"{sysinfoOut}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputSystemInfo {0}", ex.Message); - } - } - - private void outputDnsCache(string destinationFolder) { - Logger.Info("capturing dns cache information"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var dnsCache = Path.Combine(destinationFolder, "dnsCache.txt"); - Logger.Debug("running ipconfig /displaydns to {0}", dnsCache); - startInfo.Arguments = $"/C ipconfig /displaydns > \"{dnsCache}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputDnsCache {0}", ex.Message); - } - } - - private void outputExternalIP(string destinationFolder) { - Logger.Info("capturing external IP address using nslookup command"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var extIpFile = Path.Combine(destinationFolder, "externalIP.txt"); - Logger.Debug("running nslookup myip.opendns.com. resolver1.opendns.com to {0}", extIpFile); - startInfo.Arguments = $"/C nslookup myip.opendns.com. resolver1.opendns.com > \"{extIpFile}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error in outputExternalIP {0}", ex.Message); - } - } - - private void outputTasklist(string destinationFolder) { - Logger.Info("capturing executing tasks"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var tasklistOutput = Path.Combine(destinationFolder, "tasklist.txt"); - Logger.Debug("running tasklist to {0}", tasklistOutput); - startInfo.Arguments = $"/C tasklist > \"{tasklistOutput}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - private void outputRouteInfo(string destinationFolder) { - Logger.Info("capturing network routes"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var networkRoutes = Path.Combine(destinationFolder, "network-routes.txt"); - Logger.Debug("running route print to {0}", networkRoutes); - startInfo.Arguments = $"/C route print > \"{networkRoutes}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - private void outputNetstatInfo(string destinationFolder) { - Logger.Info("capturing netstat"); - try { - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - var netstatOutput = Path.Combine(destinationFolder, "netstat.txt"); - Logger.Debug("running netstat -ano to {0}", netstatOutput); - startInfo.Arguments = $"/C netstat -ano > \"{netstatOutput}\""; - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - private void outputNrpt(string destinationFolder) { - Logger.Info("outputting NRPT rules"); - try { - Logger.Info("outputting NRPT DnsClientNrptRule"); - string nrptRuleOutput = Path.Combine(destinationFolder, "NrptRule.txt"); - Process nrptRuleProcess = new Process(); - ProcessStartInfo nrptRuleStartInfo = new ProcessStartInfo(); - nrptRuleStartInfo.WindowStyle = ProcessWindowStyle.Hidden; - nrptRuleStartInfo.FileName = "cmd.exe"; - nrptRuleStartInfo.Arguments = $"/C powershell \"Get-DnsClientNrptRule | sort -Property Namespace\" > \"{nrptRuleOutput}\""; - Logger.Info("Running: {0}", nrptRuleStartInfo.Arguments); - nrptRuleProcess.StartInfo = nrptRuleStartInfo; - nrptRuleProcess.Start(); - nrptRuleProcess.WaitForExit(); - - Logger.Info("outputting NRPT DnsClientNrptPolicy"); - string nrptOutput = Path.Combine(destinationFolder, "NrptPolicy.txt"); - Process process = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - startInfo.Arguments = $"/C powershell \"Get-DnsClientNrptPolicy | sort -Property Namespace\" > \"{nrptOutput}\""; - Logger.Info("Running: {0}", startInfo.Arguments); - process.StartInfo = startInfo; - process.Start(); - process.WaitForExit(); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); - } - } - - public void Debug() { - OnStart(null);// new string[] { "FilesystemCheck" }); - } - - protected override void OnStart(string[] args) { - Logger.Debug("args: {0}", args); - Logger.Info("ziti-monitor service is starting"); - - var logs = Path.Combine(exeLocation, "logs"); - addLogsFolder(exeLocation); - addLogsFolder(logs); - addLogsFolder(Path.Combine(logs, "UI")); - addLogsFolder(Path.Combine(logs, "ZitiMonitorService")); - addLogsFolder(Path.Combine(logs, "service")); - - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService.exe.config")); //allow anyone to change the config file - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService-log.config")); //allow anyone to change the log file config - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge.exe.config")); //allow anyone to change the config file - AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge-log.config")); //allow anyone to change the log file config - - zetHealthcheck.Interval = zetHealthcheckInterval * 1000; - zetHealthcheck.Elapsed += zitiEdgeTunnelAlivenessCheck; - - Logger.Info("starting ipc server"); - ipcServer = svr.startIpcServerAsync(onIpcClientAsync); - Logger.Info("starting events server"); - eventServer = svr.startEventsServerAsync(onEventsClientAsync); - - Logger.Info("starting service watchers"); - if (!running) { - running = true; - Task.Run(() => { - SetupServiceWatchers(); - }); - } - Logger.Info("ziti-monitor service is initialized and running"); - base.OnStart(args); - } - - private void zitiEdgeTunnelAlivenessCheck(object sender, ElapsedEventArgs e) { - try { - if (zetSemaphore.Wait(TimeSpan.FromSeconds(zetHealthcheckInterval))) { - Logger.Trace("ziti-edge-tunnel aliveness check starts"); - dataClient.GetStatusAsync().Wait(); - zetSemaphore.Release(); - Interlocked.Exchange(ref zetFailedCheckCounter, 0); - Logger.Trace("ziti-edge-tunnel aliveness check ends successfully"); - } else { - Interlocked.Add(ref zetFailedCheckCounter, 1); - Logger.Warn("ziti-edge-tunnel aliveness check appears blocked and has been for {} times", zetFailedCheckCounter); - if (zetFailedCheckCounter > 2) { - disableHealthCheck(); - //after 3 failures, just terminate ziti-edge-tunnel - Interlocked.Exchange(ref zetFailedCheckCounter, 0); //reset the counter back to 0 - Logger.Warn("forcefully stopping ziti-edge-tunnel as it has been blocked for too long"); - stopProcessForcefully("ziti-edge-tunnel", "data service [ziti]"); - - Logger.Info("immediately restarting ziti-edge-tunnel"); - ServiceActions.StartService(); //attempt to start the service - } - } - } catch (Exception ex) { - Logger.Error("ziti-edge-tunnel aliveness check ends exceptionally: {}", ex.Message); - Logger.Error(ex); - } - } - - async private Task onEventsClientAsync(StreamWriter writer) { - try { - Logger.Info("a new events client was connected"); - //reset to release stream - //initial status when connecting the event stream - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 0, - Error = "", - Message = "Success", - Type = "Status", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - }; - await writer.WriteLineAsync(JsonConvert.SerializeObject(status)); - await writer.FlushAsync(); - - //if a new client attaches - send the last update check status - if (lastUpdateCheck != null) { - await writer.WriteLineAsync(JsonConvert.SerializeObject(lastInstallationNotification)); - await writer.FlushAsync(); - } - } catch (Exception ex) { - Logger.Error("UNEXPECTED ERROR: {}", ex); - } - } - -#pragma warning disable 1998 //This async method lacks 'await' - async private Task onIpcClientAsync(StreamWriter writer) { - Logger.Info("a new ipc client was connected"); - } -#pragma warning restore 1998 //This async method lacks 'await' - - private void addLogsFolder(string path) { - if (!Directory.Exists(path)) { - Logger.Info($"creating folder: {path}"); - Directory.CreateDirectory(path); - AccessUtils.GrantAccessToDirectory(path); - } - } - - public void WaitForCompletion() { - Task.WaitAll(ipcServer, eventServer); - } - - protected override void OnStop() { - Logger.Info("ziti-monitor OnStop was called"); - base.OnStop(); - } - - protected override void OnPause() { - Logger.Info("ziti-monitor OnPause was called"); - base.OnPause(); - } - - protected override void OnShutdown() { - Logger.Info("ziti-monitor OnShutdown was called"); - base.OnShutdown(); - } - - protected override void OnContinue() { - Logger.Info("ziti-monitor OnContinue was called"); - base.OnContinue(); - } - - protected override void OnCustomCommand(int command) { - Logger.Info("ziti-monitor OnCustomCommand was called {0}", command); - base.OnCustomCommand(command); - } - - protected override void OnSessionChange(SessionChangeDescription changeDescription) { - Logger.Info("ziti-monitor OnSessionChange was called {0}", changeDescription); - base.OnSessionChange(changeDescription); - } - - protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) { - Logger.Info("ziti-monitor OnPowerEvent was called {0}", powerStatus); - if (powerStatus == PowerBroadcastStatus.Suspend) { - // when going to sleep, make sure the healthcheck is disabled or accounts for going to sleep - disableHealthCheck(); - } - return base.OnPowerEvent(powerStatus); - } - - private void SetupServiceWatchers() { - var updateTimerInterval = ConfigurationManager.AppSettings.Get("UpdateTimer"); - var upInt = TimeSpan.Zero; - if (!TimeSpan.TryParse(updateTimerInterval, out upInt)) { - upInt = new TimeSpan(0, 10, 0); - } - - if (upInt.TotalMilliseconds < 10 * 60 * 1000) { - Logger.Warn("provided time [{0}] is too small. Using 10 minutes.", updateTimerInterval); -#if MOCKUPDATE || ALLOWFASTINTERVAL - Logger.Info("MOCKUPDATE detected. Not limiting check to 10 minutes"); -#else - upInt = TimeSpan.Parse("0:10:0"); -#endif - } - - _updateTimer = new System.Timers.Timer(); - _updateTimer.Elapsed += CheckUpdate; - _updateTimer.Interval = upInt.TotalMilliseconds; - _updateTimer.Enabled = true; - _updateTimer.Start(); - Logger.Info("Version Checker is running every {0} minutes", upInt.TotalMinutes); - - cleanOldLogs(asmDir); - scanForStaleDownloads(updateFolder); - - checkUpdateImmediately(); - - try { - dataClient.ConnectAsync().Wait(); - } catch { - dataClient.Reconnect(); - } - - dataClient.WaitForConnectionAsync().Wait(); - } - - private void cleanOldLogs(string whereToScan) { - //this function will be removed in the future. it's here to clean out the old ziti-monitor*log files that - //were there before the 1.5.0 release - try { - Logger.Info("Scanning for stale logs"); - foreach (var f in Directory.EnumerateFiles(whereToScan)) { - FileInfo logFile = new FileInfo(f); - if (logFile.Name.StartsWith("ziti-monitor.") && logFile.Name.EndsWith(".log")) { - Logger.Info("removing old log file: " + logFile.Name); - logFile.Delete(); - } - } - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error has occurred"); - } - } - -#if MOCKUPDATE - static DateTime mockDate = DateTime.Now; -#endif - private UpdateCheck getCheck(Version v) { -#if MOCKUPDATE - //run with MOCKUPDATE to enable debugging/mocking the update check - var check = new FilesystemCheck(v, -1, mockDate, "FilesysteCheck.download.mock.txt", new Version("2.1.4")); -#else - - if (string.IsNullOrEmpty(CurrentSettings.AutomaticUpdateURL)) { - CurrentSettings.AutomaticUpdateURL = GithubAPI.ProdUrl; - Logger.Info("Settings does not contain update url. Setting to: {}", CurrentSettings.AutomaticUpdateURL); - CurrentSettings.Write(); - } else { - Logger.Info("Settings contained a value for update url. Using: {}", CurrentSettings.AutomaticUpdateURL); - } - - var check = new GithubCheck(v, CurrentSettings.AutomaticUpdateURL); -#endif - return check; - } - - private InstallationNotificationEvent newInstallationNotificationEvent(string version) { - InstallationNotificationEvent info = new InstallationNotificationEvent() { - Code = 0, - Error = "", - Message = "InstallationUpdate", - Type = "Notification", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString().ToLower(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - ZDEVersion = version, - }; - return info; - } - - private void CheckUpdate(object sender, ElapsedEventArgs e) { - if (e != null) { - Logger.Debug("Timer triggered CheckUpdate at {0}", e.SignalTime); - } - semaphore.Wait(); - - try { - Logger.Debug("checking for update"); - var check = getCheck(assemblyVersion); - - if (check.Avail >= 0) { - Logger.Debug("update check complete. no update available"); - semaphore.Release(); - return; - } - - Logger.Info("update is available."); - if (!Directory.Exists(updateFolder)) { - Directory.CreateDirectory(updateFolder); - } - InstallationNotificationEvent info = newInstallationNotificationEvent(check.GetNextVersion().ToString()); - info.PublishTime = check.PublishDate; - info.NotificationDuration = InstallationReminder(); - if (InstallationIsCritical(check.PublishDate)) { - info.InstallTime = DateTime.Now + TimeSpan.Parse("0:0:30"); - Logger.Warn("Installation is critical! for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); - NotifyInstallationUpdates(info, true); - if (CurrentSettings.AutomaticUpdatesDisabled) { - Logger.Debug("AutomaticUpdatesDisabled is set to true. Automatic update is disabled."); - } else { - Thread.Sleep(30); - installZDE(check); - } - } else { - info.InstallTime = InstallDateFromPublishDate(check.PublishDate); - Logger.Info("Installation reminder for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); - NotifyInstallationUpdates(info); - } - lastUpdateCheck = check; - lastInstallationNotification = info; - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error has occurred during the check for ZDE updates"); - } - semaphore.Release(); - } - - private void installZDE(UpdateCheck check) { - string fileDestination = Path.Combine(updateFolder, check?.FileName); - - if (check.AlreadyDownloaded(updateFolder, check.FileName)) { - Logger.Trace("package has already been downloaded to {0}", fileDestination); - } else { - Logger.Info("copying update package begins"); - check.CopyUpdatePackage(updateFolder, check.FileName); - Logger.Info("copying update package complete"); - } - - Logger.Info("package is in {0} - moving to install phase", fileDestination); - - if (!check.HashIsValid(updateFolder, check.FileName)) { - Logger.Warn("The file was downloaded but the hash is not valid. The file will be removed: {0}", fileDestination); - File.Delete(fileDestination); - return; - } - Logger.Debug("downloaded file hash was correct. update can continue."); -#if !SKIPUPDATE - try { - Logger.Info("verifying file [{}]", fileDestination); - new SignedFileValidator(fileDestination).Verify(); - Logger.Info("SignedFileValidator complete"); - - StopZiti(); - StopUI().Wait(); - - Logger.Info("Running update package: " + fileDestination); - // shell out to a new process and run the uninstall, reinstall steps which SHOULD stop this current process as well - Process.Start(fileDestination, "/passive"); - } catch (Exception ex) { - Logger.Error(ex, "Unexpected error during installation"); - } -#else - Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); - Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); - Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); -#endif - } - - private bool isOlder(Version current) { - int compare = current.CompareTo(assemblyVersion); - Logger.Info("comparing current[{0}] to compare[{1}]: {2}", current.ToString(), assemblyVersion.ToString(), compare); - if (compare < 0) { - return true; - } else if (compare > 0) { - return false; - } else { - return false; - } - } - - private void scanForStaleDownloads(string folder) { - try { - if (!Directory.Exists(folder)) { - Logger.Debug("folder {0} does not exist. skipping", folder); - return; - } - Logger.Info("Scanning for stale downloads"); - foreach (var f in Directory.EnumerateFiles(folder)) { - try { - FileInfo fi = new FileInfo(f); - if (fi.Exists) { - if (fi.Name.StartsWith(filePrefix)) { - Logger.Debug("scanning for staleness: " + f); - string ver = Path.GetFileNameWithoutExtension(f).Substring(filePrefix.Length); - Version fileVersion = Version.Parse(ver); - if (isOlder(fileVersion)) { - Logger.Info("Removing old download: " + fi.Name); - fi.Delete(); - } else { - Logger.Debug("Retaining file. {1} is the same or newer than {1}", fi.Name, assemblyVersion); - } - } else { - Logger.Debug("skipping file named {0}", f); - } - } else { - Logger.Debug("file named {0} did not exist?", f); - } - } catch (Exception ex) { - Logger.Error(ex, "Unexpected exception processing {0}", f); - } - } - } catch (Exception ex) { - Logger.Error(ex, "Unexpected exception"); - } - } - - private void StopZiti() { - Logger.Info("Stopping the ziti service..."); - controller = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == "ziti"); - bool cleanStop = false; - if (controller != null && controller.Status != ServiceControllerStatus.Stopped) { - try { - controller.Stop(); - Logger.Debug("Waiting for the ziti service to stop."); - controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); - Logger.Debug("The ziti service was stopped successfully."); - cleanStop = true; - } catch (Exception e) { - Logger.Error(e, "Timout while trying to stop service!"); - } - } else { - Logger.Debug("The ziti has ALREADY been stopped successfully."); - } - if (!cleanStop) { - Logger.Debug("Stopping ziti-edge-tunnel forcefully."); - stopProcessForcefully("ziti-edge-tunnel", "data service [ziti]"); - } - } - - private void stopProcessForcefully(string processName, string description) { - try { - Logger.Info("Closing the {description} process", description); - Process[] workers = Process.GetProcessesByName(processName); - if (workers.Length < 1) { - Logger.Info("No {description} process found to close.", description); - return; - } - // though strange, because we're about to kill the process, this is still - // considered 'expected' since the monitor service is shutting it down (forcefully). - // not clean is to indicate the process ended unexpectedly - dataClient.ExpectedShutdown = true; - - foreach (Process worker in workers) { - try { - Logger.Info("Killing: {0}", worker); - if (!worker.CloseMainWindow()) { - //don't care right now because when called on the UI it just gets 'hidden' - } - worker.Kill(); - worker.WaitForExit(5000); - Logger.Info("Stopping the {description} process killed", description); - worker.Dispose(); - } catch (Exception e) { - Logger.Error(e, "Unexpected error when closing the {description}!", description); - } - } - } catch (Exception e) { - Logger.Error(e, "Unexpected error when closing the {description}!", description); - } - } - - async private Task StopUI() { - //first try to ask the UI to exit: - - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 0, - Error = "", - Message = "Upgrading" - }; - EventRegistry.SendEventToConsumers(status); - - await Task.Delay(1000); //wait for the event to send and give the UI time to close... - - stopProcessForcefully("ZitiDesktopEdge", "UI"); - } - - private static void Svc_OnShutdownEvent(object sender, StatusEvent e) { - Logger.Info("the service is shutting down normally..."); - - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 0, - Error = "SERVICE DOWN", - Message = "SERVICE DOWN", - Status = ServiceActions.ServiceStatus() - }; - EventRegistry.SendEventToConsumers(status); - } - - private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { - SetLogLevel(e.LogLevel); - } - - private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { - Logger.Trace("Notification event but not acting: {0}", e.Op); - } - - private void Svc_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { - string dns = e?.Status?.IpInfo?.DNS; - string version = e?.Status?.ServiceVersion.Version; - string op = e?.Op; - Logger.Info($"Operation {op}. running dns: {dns} at version {version}"); - - SetLogLevel(e.Status.LogLevel); - } - - private void disableHealthCheck() { - if (zetHealthcheck.Enabled) { - zetHealthcheck.Enabled = true; - zetHealthcheck.Stop(); - Logger.Info("ziti-edge-tunnel health check disabled"); - } else { - Logger.Info("ziti-edge-tunnel health check already disabled"); - } - } - - private void enableHealthCheck() { - if (!zetHealthcheck.Enabled) { - zetHealthcheck.Enabled = false; - zetHealthcheck.Start(); - Logger.Info("ziti-edge-tunnel health check enabled"); - } else { - Logger.Info("ziti-edge-tunnel health check already enabled"); - } - } - - private void Svc_OnClientConnected(object sender, object e) { - Logger.Info("successfully connected to service"); - enableHealthCheck(); - } - - private void Svc_OnClientDisconnected(object sender, object e) { - disableHealthCheck(); //no need to healthcheck when we know it's disconnected - DataClient svc = (DataClient)sender; - if (svc.ExpectedShutdown) { - //then this is fine and expected - the service is shutting down - Logger.Info("client disconnected due to clean service shutdown"); - } else { - Logger.Error("SERVICE IS DOWN and did not exit cleanly."); - - MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { - Code = 10, - Error = "SERVICE DOWN", - Message = "SERVICE DOWN", - Type = "Status", - Status = ServiceActions.ServiceStatus(), - ReleaseStream = IsBeta ? "beta" : "stable", - AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), - AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, - }; - EventRegistry.SendEventToConsumers(status); - } - } - - private static void EnumerateDNS() { - var ps = System.Management.Automation.PowerShell.Create(); - ps.AddScript("Get-DnsClientServerAddress"); - var results = ps.Invoke(); - - using (StringWriter sw = new StringWriter()) { - foreach (var r in results) { - string name = (string)r.Properties["InterfaceAlias"].Value; - string[] dnses = (string[])r.Properties["ServerAddresses"].Value; - sw.WriteLine($"Interface: {name}. DNS: {string.Join(",", dnses)}"); - } - Logger.Info("DNS RESULTS:\n{0}", sw.ToString()); - } - } - - private TimeSpan InstallationReminder() { - var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationReminder"); - var reminderInt = TimeSpan.Zero; - if (!TimeSpan.TryParse(installationReminderIntervalStr, out reminderInt)) { - reminderInt = new TimeSpan(0, 1, 0); - } - return reminderInt; - } - - private DateTime InstallDateFromPublishDate(DateTime publishDate) { - var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); - var instCritTimespan = TimeSpan.Zero; - if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { - instCritTimespan = TimeSpan.Parse("7:0:0:0"); - } - return publishDate + instCritTimespan; - } - - private bool InstallationIsCritical(DateTime publishDate) { - var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); - var instCritTimespan = TimeSpan.Zero; - if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { - instCritTimespan = TimeSpan.Parse("7:0:0:0"); - } - return DateTime.Now > publishDate + instCritTimespan; - } - - private void NotifyInstallationUpdates(InstallationNotificationEvent evt) { - NotifyInstallationUpdates(evt, false); - } - - private void NotifyInstallationUpdates(InstallationNotificationEvent evt, bool force) { - try { - evt.Message = "InstallationUpdate"; - evt.Type = "Notification"; - EventRegistry.SendEventToConsumers(evt); - Logger.Debug("NotifyInstallationUpdates: sent for version {0} is sent to the events pipe...", evt.ZDEVersion); - return; - } catch (Exception e) { - Logger.Error("The notification for the installation updates for version {0} has failed: {1}", evt.ZDEVersion, e); - } - } - } -} \ No newline at end of file +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Diagnostics; +using System.Linq; +using System.ServiceProcess; +using System.IO; +using System.Timers; +using System.Configuration; +using System.Threading.Tasks; +using System.Threading; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.IO.Compression; + +using ZitiDesktopEdge.DataStructures; +using ZitiDesktopEdge.ServiceClient; +using ZitiDesktopEdge.Server; +using ZitiDesktopEdge.Utility; + +using NLog; +using Newtonsoft.Json; +using System.Net; +using DnsClient; +using DnsClient.Protocol; +using ZitiUpdateService.Utils; +using ZitiUpdateService.Checkers; +using System.Security.Policy; +using Newtonsoft.Json.Linq; +using System.Runtime.Remoting.Messaging; + +#if !SKIPUPDATE +using ZitiUpdateService.Checkers.PeFile; +#endif + +namespace ZitiUpdateService { + public partial class UpdateService : ServiceBase { + private const string betaStreamMarkerFile = "use-beta-stream.txt"; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static Settings CurrentSettings = new Settings(true); + + public bool IsBeta { + get { + return File.Exists(Path.Combine(exeLocation, betaStreamMarkerFile)); + } + private set { } + } + + + private System.Timers.Timer _updateTimer = new System.Timers.Timer(); + private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); + + private string exeLocation = null; + + private DataClient dataClient = new DataClient("monitor service"); + private bool running = false; + private string asmDir = null; + private string updateFolder = null; + private string filePrefix = "Ziti.Desktop.Edge.Client-"; + private Version assemblyVersion = null; + + private ServiceController controller; + private IPCServer svr = new IPCServer(); + private Task ipcServer = null; + private Task eventServer = null; + + private const int zetHealthcheckInterval = 5; + private SemaphoreSlim zetSemaphore = new SemaphoreSlim(1, 1); + private System.Timers.Timer zetHealthcheck = new System.Timers.Timer(); + private int zetFailedCheckCounter = 0; + + private UpdateCheck lastUpdateCheck; + private InstallationNotificationEvent lastInstallationNotification; + + public UpdateService() { + InitializeComponent(); + + CurrentSettings.Load(); + CurrentSettings.OnConfigurationChange += CurrentSettings_OnConfigurationChange; + + base.CanHandlePowerEvent = true; + + exeLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + Logger.Info("Initializing"); + dataClient.OnClientConnected += Svc_OnClientConnected; + dataClient.OnTunnelStatusEvent += Svc_OnTunnelStatusEvent; + dataClient.OnClientDisconnected += Svc_OnClientDisconnected; + dataClient.OnShutdownEvent += Svc_OnShutdownEvent; + dataClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; + dataClient.OnNotificationEvent += ServiceClient_OnNotificationEvent; + + svr.CaptureLogs = CaptureLogs; + svr.SetLogLevel = SetLogLevel; + svr.SetReleaseStream = SetReleaseStream; + svr.DoUpdateCheck = DoUpdateCheck; + svr.TriggerUpdate = TriggerUpdate; + svr.SetAutomaticUpdateDisabled = SetAutomaticUpdateDisabled; + svr.SetAutomaticUpdateURL = SetAutomaticUpdateURL; + + string assemblyVersionStr = Assembly.GetExecutingAssembly().GetName().Version.ToString(); //fetch from ziti? + assemblyVersion = new Version(assemblyVersionStr); + asmDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + updateFolder = Path.Combine(asmDir, "updates"); + if (!Directory.Exists(updateFolder)) { + Directory.CreateDirectory(updateFolder); + } + } + + private SvcResponse SetAutomaticUpdateURL(string url) { + SvcResponse failure = new SvcResponse(); + failure.Code = (int)ErrorCodes.URL_INVALID; + failure.Error = $"The url supplied is invalid: \n{url}\n"; + failure.Message = "Failure"; + + + SvcResponse r = new SvcResponse(); + if (url == null || !url.StartsWith("http")) { + return failure; + } else { + // check the url exists and appears correct... + var check = new GithubCheck(assemblyVersion, url); + + if (check != null) { + var v = check.GetNextVersion(); + + if (v == null) { + return failure; + } + if (v.Revision.ToString().Trim() == "") { + return failure; + } + } + + checkUpdateImmediately(); + + CurrentSettings.AutomaticUpdateURL = url; + CurrentSettings.Write(); + r.Message = "Success"; + } + return r; + } + + private void CurrentSettings_OnConfigurationChange(object sender, ControllerEvent e) { + MonitorServiceStatusEvent evt; + if (lastInstallationNotification != null) { + evt = lastInstallationNotification; + } else { + evt = new MonitorServiceStatusEvent() { + Code = 0, + Error = "", + Message = "Configuration Changed", + Type = "Status", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + }; + } + Logger.Debug($"notifying consumers of change to CurrentSettings. AutomaticUpdates status = {(CurrentSettings.AutomaticUpdatesDisabled ? "disabled" : "enabled")}"); + EventRegistry.SendEventToConsumers(evt); + } + + private SvcResponse SetAutomaticUpdateDisabled(bool disabled) { + if (lastInstallationNotification != null) { + lastInstallationNotification.AutomaticUpgradeDisabled = disabled.ToString(); + } + CurrentSettings.AutomaticUpdatesDisabled = disabled; + CurrentSettings.Write(); + SvcResponse r = new SvcResponse(); + r.Message = "Success"; + return r; + } + + private SvcResponse TriggerUpdate() { + SvcResponse r = new SvcResponse(); + r.Message = "Initiating Update"; + + Task.Run(() => { installZDE(lastUpdateCheck); }); + return r; + } + + + private void checkUpdateImmediately() { + try { + CheckUpdate(null, null); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in CheckUpdate"); + } + } + + private StatusCheck DoUpdateCheck() { + StatusCheck r = new StatusCheck(); + + UpdateCheck check = getCheck(assemblyVersion); + + r.Code = check.Avail; + r.ReleaseStream = IsBeta ? "beta" : "stable"; + switch (r.Code) { + case -1: + r.Message = $"An update is available: {check.GetNextVersion()}"; + r.UpdateAvailable = true; + Logger.Debug("Update {0} is published on {1}", check.GetNextVersion(), check.PublishDate); + checkUpdateImmediately(); + break; + case 0: + r.Message = $"The current version [{assemblyVersion}] is the latest"; + break; + case 1: + r.Message = $"Your version [{assemblyVersion}] is newer than the latest release"; + break; + default: + r.Message = "Update check failed"; + break; + } + return r; + } + + private void SetLogLevel(string level) { + try { + Logger.Info("request to change log level received: {0}", level); + if (("" + level).ToLower().Trim() == "verbose") { + level = "trace"; + Logger.Info("request to change log level to verbose - but using trace instead"); + } + var l = LogLevel.FromString(level); + foreach (var rule in LogManager.Configuration.LoggingRules) { + rule.EnableLoggingForLevel(l); + rule.SetLoggingLevels(l, LogLevel.Fatal); + } + + LogManager.ReconfigExistingLoggers(); + Logger.Info("logger reconfigured to log at level: {0}", l); + } catch (Exception e) { + Logger.Error(e, "Could NOT set the log level for loggers??? {0}", e.Message); + } + } + + private void SetReleaseStream(string stream) { + string markerFile = Path.Combine(exeLocation, betaStreamMarkerFile); + if (stream == "beta") { + if (IsBeta) { + Logger.Debug("already using beta stream. No action taken"); + } else { + Logger.Info("Setting update service to use beta stream!"); + using (File.Create(markerFile)) { + + } + AccessUtils.GrantAccessToFile(markerFile); //allow anyone to delete this manually if need be... + Logger.Debug("added marker file: {0}", markerFile); + } + } else { + if (!IsBeta) { + Logger.Debug("already using release stream. No action taken"); + } else { + Logger.Info("Setting update service to use release stream!"); + if (File.Exists(markerFile)) { + File.Delete(markerFile); + Logger.Debug("removed marker file: {0}", markerFile); + } + } + } + } + + private string CaptureLogs() { + try { + string logLocation = Path.Combine(exeLocation, "logs"); + string destinationLocation = Path.Combine(exeLocation, "temp"); + string serviceLogsLocation = Path.Combine(logLocation, "service"); + string serviceLogsDest = Path.Combine(destinationLocation, "service"); + + Logger.Debug("removing leftover temp folder: {0}", destinationLocation); + try { + Directory.Delete(destinationLocation, true); + } catch { + //means it doesn't exist + } + + Directory.CreateDirectory(destinationLocation); + + Logger.Debug("copying all directories from: {0}", logLocation); + foreach (string dirPath in Directory.GetDirectories(logLocation, "*", SearchOption.AllDirectories)) { + Directory.CreateDirectory(dirPath.Replace(logLocation, destinationLocation)); + } + + Logger.Debug("copying all non-zip files from: {0}", logLocation); + foreach (string newPath in Directory.GetFiles(logLocation, "*.*", SearchOption.AllDirectories)) { + if (!newPath.EndsWith(".zip")) { + File.Copy(newPath, newPath.Replace(logLocation, destinationLocation), true); + } + } + + Logger.Debug("copying service files from: {0} to {1}", serviceLogsLocation, serviceLogsDest); + Directory.CreateDirectory(serviceLogsDest); + foreach (string newPath in Directory.GetFiles(serviceLogsLocation, "*.*", SearchOption.TopDirectoryOnly)) { + if (newPath.EndsWith(".log") || newPath.Contains("config.json")) { + Logger.Debug("copying service log: {0}", newPath); + File.Copy(newPath, newPath.Replace(serviceLogsLocation, serviceLogsDest), true); + } + } + + outputIpconfigInfo(destinationLocation); + outputSystemInfo(destinationLocation); + outputDnsCache(destinationLocation); + outputExternalIP(destinationLocation); + outputTasklist(destinationLocation); + outputRouteInfo(destinationLocation); + outputNetstatInfo(destinationLocation); + outputNrpt(destinationLocation); + + Task.Delay(500).Wait(); + + string zipName = Path.Combine(logLocation, DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".zip"); + ZipFile.CreateFromDirectory(destinationLocation, zipName); + + Logger.Debug("cleaning up temp folder: {0}", destinationLocation); + try { + Directory.Delete(destinationLocation, true); + } catch { + //means it doesn't exist + } + return zipName; + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in generating system files {0}", ex.Message); + return null; + } + } + + private void outputIpconfigInfo(string destinationFolder) { + Logger.Info("capturing ipconfig information"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var ipconfigOut = Path.Combine(destinationFolder, "ipconfig.all.txt"); + Logger.Debug("copying ipconfig /all to {0}", ipconfigOut); + startInfo.Arguments = $"/C ipconfig /all > \"{ipconfigOut}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputIpconfigInfo {0}", ex.Message); + } + } + + private void outputSystemInfo(string destinationFolder) { + Logger.Info("capturing systeminfo"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var sysinfoOut = Path.Combine(destinationFolder, "systeminfo.txt"); + Logger.Debug("running systeminfo to {0}", sysinfoOut); + startInfo.Arguments = $"/C systeminfo > \"{sysinfoOut}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputSystemInfo {0}", ex.Message); + } + } + + private void outputDnsCache(string destinationFolder) { + Logger.Info("capturing dns cache information"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var dnsCache = Path.Combine(destinationFolder, "dnsCache.txt"); + Logger.Debug("running ipconfig /displaydns to {0}", dnsCache); + startInfo.Arguments = $"/C ipconfig /displaydns > \"{dnsCache}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputDnsCache {0}", ex.Message); + } + } + + private void outputExternalIP(string destinationFolder) { + Logger.Info("capturing external IP address using nslookup command"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var extIpFile = Path.Combine(destinationFolder, "externalIP.txt"); + Logger.Debug("running nslookup myip.opendns.com. resolver1.opendns.com to {0}", extIpFile); + startInfo.Arguments = $"/C nslookup myip.opendns.com. resolver1.opendns.com > \"{extIpFile}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error in outputExternalIP {0}", ex.Message); + } + } + + private void outputTasklist(string destinationFolder) { + Logger.Info("capturing executing tasks"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var tasklistOutput = Path.Combine(destinationFolder, "tasklist.txt"); + Logger.Debug("running tasklist to {0}", tasklistOutput); + startInfo.Arguments = $"/C tasklist > \"{tasklistOutput}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + private void outputRouteInfo(string destinationFolder) { + Logger.Info("capturing network routes"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var networkRoutes = Path.Combine(destinationFolder, "network-routes.txt"); + Logger.Debug("running route print to {0}", networkRoutes); + startInfo.Arguments = $"/C route print > \"{networkRoutes}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + private void outputNetstatInfo(string destinationFolder) { + Logger.Info("capturing netstat"); + try { + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + var netstatOutput = Path.Combine(destinationFolder, "netstat.txt"); + Logger.Debug("running netstat -ano to {0}", netstatOutput); + startInfo.Arguments = $"/C netstat -ano > \"{netstatOutput}\""; + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + private void outputNrpt(string destinationFolder) { + Logger.Info("outputting NRPT rules"); + try { + Logger.Info("outputting NRPT DnsClientNrptRule"); + string nrptRuleOutput = Path.Combine(destinationFolder, "NrptRule.txt"); + Process nrptRuleProcess = new Process(); + ProcessStartInfo nrptRuleStartInfo = new ProcessStartInfo(); + nrptRuleStartInfo.WindowStyle = ProcessWindowStyle.Hidden; + nrptRuleStartInfo.FileName = "cmd.exe"; + nrptRuleStartInfo.Arguments = $"/C powershell \"Get-DnsClientNrptRule | sort -Property Namespace\" > \"{nrptRuleOutput}\""; + Logger.Info("Running: {0}", nrptRuleStartInfo.Arguments); + nrptRuleProcess.StartInfo = nrptRuleStartInfo; + nrptRuleProcess.Start(); + nrptRuleProcess.WaitForExit(); + + Logger.Info("outputting NRPT DnsClientNrptPolicy"); + string nrptOutput = Path.Combine(destinationFolder, "NrptPolicy.txt"); + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + startInfo.Arguments = $"/C powershell \"Get-DnsClientNrptPolicy | sort -Property Namespace\" > \"{nrptOutput}\""; + Logger.Info("Running: {0}", startInfo.Arguments); + process.StartInfo = startInfo; + process.Start(); + process.WaitForExit(); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error {0}", ex.Message); + } + } + + public void Debug() { + OnStart(null);// new string[] { "FilesystemCheck" }); + } + + protected override void OnStart(string[] args) { + Logger.Debug("args: {0}", args); + Logger.Info("ziti-monitor service is starting"); + + var logs = Path.Combine(exeLocation, "logs"); + addLogsFolder(exeLocation); + addLogsFolder(logs); + addLogsFolder(Path.Combine(logs, "UI")); + addLogsFolder(Path.Combine(logs, "ZitiMonitorService")); + addLogsFolder(Path.Combine(logs, "service")); + + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService.exe.config")); //allow anyone to change the config file + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiUpdateService-log.config")); //allow anyone to change the log file config + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge.exe.config")); //allow anyone to change the config file + AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge-log.config")); //allow anyone to change the log file config + + zetHealthcheck.Interval = zetHealthcheckInterval * 1000; + zetHealthcheck.Elapsed += zitiEdgeTunnelAlivenessCheck; + + Logger.Info("starting ipc server"); + ipcServer = svr.startIpcServerAsync(onIpcClientAsync); + Logger.Info("starting events server"); + eventServer = svr.startEventsServerAsync(onEventsClientAsync); + + Logger.Info("starting service watchers"); + if (!running) { + running = true; + Task.Run(() => { + SetupServiceWatchers(); + }); + } + Logger.Info("ziti-monitor service is initialized and running"); + base.OnStart(args); + } + + private void zitiEdgeTunnelAlivenessCheck(object sender, ElapsedEventArgs e) { + try { + if (zetSemaphore.Wait(TimeSpan.FromSeconds(zetHealthcheckInterval))) { + Logger.Trace("ziti-edge-tunnel aliveness check starts"); + dataClient.GetStatusAsync().Wait(); + zetSemaphore.Release(); + Interlocked.Exchange(ref zetFailedCheckCounter, 0); + Logger.Trace("ziti-edge-tunnel aliveness check ends successfully"); + } else { + Interlocked.Add(ref zetFailedCheckCounter, 1); + Logger.Warn("ziti-edge-tunnel aliveness check appears blocked and has been for {} times", zetFailedCheckCounter); + if (zetFailedCheckCounter > 2) { + disableHealthCheck(); + //after 3 failures, just terminate ziti-edge-tunnel + Interlocked.Exchange(ref zetFailedCheckCounter, 0); //reset the counter back to 0 + Logger.Warn("forcefully stopping ziti-edge-tunnel as it has been blocked for too long"); + stopProcessForcefully("ziti-edge-tunnel", "data service [ziti]"); + + Logger.Info("immediately restarting ziti-edge-tunnel"); + ServiceActions.StartService(); //attempt to start the service + } + } + } catch (Exception ex) { + Logger.Error("ziti-edge-tunnel aliveness check ends exceptionally: {}", ex.Message); + Logger.Error(ex); + } + } + + async private Task onEventsClientAsync(StreamWriter writer) { + try { + Logger.Info("a new events client was connected"); + //reset to release stream + //initial status when connecting the event stream + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 0, + Error = "", + Message = "Success", + Type = "Status", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + }; + await writer.WriteLineAsync(JsonConvert.SerializeObject(status)); + await writer.FlushAsync(); + + //if a new client attaches - send the last update check status + if (lastUpdateCheck != null) { + await writer.WriteLineAsync(JsonConvert.SerializeObject(lastInstallationNotification)); + await writer.FlushAsync(); + } + } catch (Exception ex) { + Logger.Error("UNEXPECTED ERROR: {}", ex); + } + } + +#pragma warning disable 1998 //This async method lacks 'await' + async private Task onIpcClientAsync(StreamWriter writer) { + Logger.Info("a new ipc client was connected"); + } +#pragma warning restore 1998 //This async method lacks 'await' + + private void addLogsFolder(string path) { + if (!Directory.Exists(path)) { + Logger.Info($"creating folder: {path}"); + Directory.CreateDirectory(path); + AccessUtils.GrantAccessToDirectory(path); + } + } + + public void WaitForCompletion() { + Task.WaitAll(ipcServer, eventServer); + } + + protected override void OnStop() { + Logger.Info("ziti-monitor OnStop was called"); + base.OnStop(); + } + + protected override void OnPause() { + Logger.Info("ziti-monitor OnPause was called"); + base.OnPause(); + } + + protected override void OnShutdown() { + Logger.Info("ziti-monitor OnShutdown was called"); + base.OnShutdown(); + } + + protected override void OnContinue() { + Logger.Info("ziti-monitor OnContinue was called"); + base.OnContinue(); + } + + protected override void OnCustomCommand(int command) { + Logger.Info("ziti-monitor OnCustomCommand was called {0}", command); + base.OnCustomCommand(command); + } + + protected override void OnSessionChange(SessionChangeDescription changeDescription) { + Logger.Info("ziti-monitor OnSessionChange was called {0}", changeDescription); + base.OnSessionChange(changeDescription); + } + + protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) { + Logger.Info("ziti-monitor OnPowerEvent was called {0}", powerStatus); + if (powerStatus == PowerBroadcastStatus.Suspend) { + // when going to sleep, make sure the healthcheck is disabled or accounts for going to sleep + disableHealthCheck(); + } + return base.OnPowerEvent(powerStatus); + } + + private void SetupServiceWatchers() { + var updateTimerInterval = ConfigurationManager.AppSettings.Get("UpdateTimer"); + var upInt = TimeSpan.Zero; + if (!TimeSpan.TryParse(updateTimerInterval, out upInt)) { + upInt = new TimeSpan(0, 10, 0); + } + + if (upInt.TotalMilliseconds < 10 * 60 * 1000) { + Logger.Warn("provided time [{0}] is too small. Using 10 minutes.", updateTimerInterval); +#if MOCKUPDATE || ALLOWFASTINTERVAL + Logger.Info("MOCKUPDATE detected. Not limiting check to 10 minutes"); +#else + upInt = TimeSpan.Parse("0:10:0"); +#endif + } + + _updateTimer = new System.Timers.Timer(); + _updateTimer.Elapsed += CheckUpdate; + _updateTimer.Interval = upInt.TotalMilliseconds; + _updateTimer.Enabled = true; + _updateTimer.Start(); + Logger.Info("Version Checker is running every {0} minutes", upInt.TotalMinutes); + + cleanOldLogs(asmDir); + scanForStaleDownloads(updateFolder); + + checkUpdateImmediately(); + + try { + dataClient.ConnectAsync().Wait(); + } catch { + dataClient.Reconnect(); + } + + dataClient.WaitForConnectionAsync().Wait(); + } + + private void cleanOldLogs(string whereToScan) { + //this function will be removed in the future. it's here to clean out the old ziti-monitor*log files that + //were there before the 1.5.0 release + try { + Logger.Info("Scanning for stale logs"); + foreach (var f in Directory.EnumerateFiles(whereToScan)) { + FileInfo logFile = new FileInfo(f); + if (logFile.Name.StartsWith("ziti-monitor.") && logFile.Name.EndsWith(".log")) { + Logger.Info("removing old log file: " + logFile.Name); + logFile.Delete(); + } + } + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error has occurred"); + } + } + +#if MOCKUPDATE + static DateTime mockDate = DateTime.Now; +#endif + private UpdateCheck getCheck(Version v) { +#if MOCKUPDATE + //run with MOCKUPDATE to enable debugging/mocking the update check + var check = new FilesystemCheck(v, -1, mockDate, "FilesysteCheck.download.mock.txt", new Version("2.1.4")); +#else + + if (string.IsNullOrEmpty(CurrentSettings.AutomaticUpdateURL)) { + CurrentSettings.AutomaticUpdateURL = GithubAPI.ProdUrl; + Logger.Info("Settings does not contain update url. Setting to: {}", CurrentSettings.AutomaticUpdateURL); + CurrentSettings.Write(); + } else { + Logger.Info("Settings contained a value for update url. Using: {}", CurrentSettings.AutomaticUpdateURL); + } + + var check = new GithubCheck(v, CurrentSettings.AutomaticUpdateURL); +#endif + return check; + } + + private InstallationNotificationEvent newInstallationNotificationEvent(string version) { + InstallationNotificationEvent info = new InstallationNotificationEvent() { + Code = 0, + Error = "", + Message = "InstallationUpdate", + Type = "Notification", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString().ToLower(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + ZDEVersion = version, + }; + return info; + } + + private void CheckUpdate(object sender, ElapsedEventArgs e) { + if (e != null) { + Logger.Debug("Timer triggered CheckUpdate at {0}", e.SignalTime); + } + semaphore.Wait(); + + try { + Logger.Debug("checking for update"); + var check = getCheck(assemblyVersion); + + if (check.Avail >= 0) { + Logger.Debug("update check complete. no update available"); + semaphore.Release(); + return; + } + + Logger.Info("update is available."); + if (!Directory.Exists(updateFolder)) { + Directory.CreateDirectory(updateFolder); + } + InstallationNotificationEvent info = newInstallationNotificationEvent(check.GetNextVersion().ToString()); + info.PublishTime = check.PublishDate; + info.NotificationDuration = InstallationReminder(); + if (InstallationIsCritical(check.PublishDate)) { + info.InstallTime = DateTime.Now + TimeSpan.Parse("0:0:30"); + Logger.Warn("Installation is critical! for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); + NotifyInstallationUpdates(info, true); + if (CurrentSettings.AutomaticUpdatesDisabled) { + Logger.Debug("AutomaticUpdatesDisabled is set to true. Automatic update is disabled."); + } else { + Thread.Sleep(30); + installZDE(check); + } + } else { + info.InstallTime = InstallDateFromPublishDate(check.PublishDate); + Logger.Info("Installation reminder for ZDE version: {0}. update published at: {1}. approximate install time: {2}", info.ZDEVersion, check.PublishDate, info.InstallTime); + NotifyInstallationUpdates(info); + } + lastUpdateCheck = check; + lastInstallationNotification = info; + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error has occurred during the check for ZDE updates"); + } + semaphore.Release(); + } + + private void installZDE(UpdateCheck check) { + string fileDestination = Path.Combine(updateFolder, check?.FileName); + + if (check.AlreadyDownloaded(updateFolder, check.FileName)) { + Logger.Trace("package has already been downloaded to {0}", fileDestination); + } else { + Logger.Info("copying update package begins"); + check.CopyUpdatePackage(updateFolder, check.FileName); + Logger.Info("copying update package complete"); + } + + Logger.Info("package is in {0} - moving to install phase", fileDestination); + + if (!check.HashIsValid(updateFolder, check.FileName)) { + Logger.Warn("The file was downloaded but the hash is not valid. The file will be removed: {0}", fileDestination); + File.Delete(fileDestination); + return; + } + Logger.Debug("downloaded file hash was correct. update can continue."); +#if !SKIPUPDATE + try { + Logger.Info("verifying file [{}]", fileDestination); + new SignedFileValidator(fileDestination).Verify(); + Logger.Info("SignedFileValidator complete"); + + StopZiti(); + StopUI().Wait(); + + Logger.Info("Running update package: " + fileDestination); + // shell out to a new process and run the uninstall, reinstall steps which SHOULD stop this current process as well + Process.Start(fileDestination, "/passive"); + } catch (Exception ex) { + Logger.Error(ex, "Unexpected error during installation"); + } +#else + Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); + Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); + Logger.Warn("SKIPUPDATE IS SET - NOT PERFORMING UPDATE of version: {} published at {}", check.GetNextVersion(), check.PublishDate); +#endif + } + + private bool isOlder(Version current) { + int compare = current.CompareTo(assemblyVersion); + Logger.Info("comparing current[{0}] to compare[{1}]: {2}", current.ToString(), assemblyVersion.ToString(), compare); + if (compare < 0) { + return true; + } else if (compare > 0) { + return false; + } else { + return false; + } + } + + private void scanForStaleDownloads(string folder) { + try { + if (!Directory.Exists(folder)) { + Logger.Debug("folder {0} does not exist. skipping", folder); + return; + } + Logger.Info("Scanning for stale downloads"); + foreach (var f in Directory.EnumerateFiles(folder)) { + try { + FileInfo fi = new FileInfo(f); + if (fi.Exists) { + if (fi.Name.StartsWith(filePrefix)) { + Logger.Debug("scanning for staleness: " + f); + string ver = Path.GetFileNameWithoutExtension(f).Substring(filePrefix.Length); + Version fileVersion = Version.Parse(ver); + if (isOlder(fileVersion)) { + Logger.Info("Removing old download: " + fi.Name); + fi.Delete(); + } else { + Logger.Debug("Retaining file. {1} is the same or newer than {1}", fi.Name, assemblyVersion); + } + } else { + Logger.Debug("skipping file named {0}", f); + } + } else { + Logger.Debug("file named {0} did not exist?", f); + } + } catch (Exception ex) { + Logger.Error(ex, "Unexpected exception processing {0}", f); + } + } + } catch (Exception ex) { + Logger.Error(ex, "Unexpected exception"); + } + } + + private void StopZiti() { + Logger.Info("Stopping the ziti service..."); + controller = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == "ziti"); + bool cleanStop = false; + if (controller != null && controller.Status != ServiceControllerStatus.Stopped) { + try { + controller.Stop(); + Logger.Debug("Waiting for the ziti service to stop."); + controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); + Logger.Debug("The ziti service was stopped successfully."); + cleanStop = true; + } catch (Exception e) { + Logger.Error(e, "Timout while trying to stop service!"); + } + } else { + Logger.Debug("The ziti has ALREADY been stopped successfully."); + } + if (!cleanStop) { + Logger.Debug("Stopping ziti-edge-tunnel forcefully."); + stopProcessForcefully("ziti-edge-tunnel", "data service [ziti]"); + } + } + + private void stopProcessForcefully(string processName, string description) { + try { + Logger.Info("Closing the {description} process", description); + Process[] workers = Process.GetProcessesByName(processName); + if (workers.Length < 1) { + Logger.Info("No {description} process found to close.", description); + return; + } + // though strange, because we're about to kill the process, this is still + // considered 'expected' since the monitor service is shutting it down (forcefully). + // not clean is to indicate the process ended unexpectedly + dataClient.ExpectedShutdown = true; + + foreach (Process worker in workers) { + try { + Logger.Info("Killing: {0}", worker); + if (!worker.CloseMainWindow()) { + //don't care right now because when called on the UI it just gets 'hidden' + } + worker.Kill(); + worker.WaitForExit(5000); + Logger.Info("Stopping the {description} process killed", description); + worker.Dispose(); + } catch (Exception e) { + Logger.Error(e, "Unexpected error when closing the {description}!", description); + } + } + } catch (Exception e) { + Logger.Error(e, "Unexpected error when closing the {description}!", description); + } + } + + async private Task StopUI() { + //first try to ask the UI to exit: + + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 0, + Error = "", + Message = "Upgrading" + }; + EventRegistry.SendEventToConsumers(status); + + await Task.Delay(1000); //wait for the event to send and give the UI time to close... + + stopProcessForcefully("ZitiDesktopEdge", "UI"); + } + + private static void Svc_OnShutdownEvent(object sender, StatusEvent e) { + Logger.Info("the service is shutting down normally..."); + + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 0, + Error = "SERVICE DOWN", + Message = "SERVICE DOWN", + Status = ServiceActions.ServiceStatus() + }; + EventRegistry.SendEventToConsumers(status); + } + + private void ServiceClient_OnLogLevelEvent(object sender, LogLevelEvent e) { + SetLogLevel(e.LogLevel); + } + + private void ServiceClient_OnNotificationEvent(object sender, NotificationEvent e) { + Logger.Trace("Notification event but not acting: {0}", e.Op); + } + + private void Svc_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { + string dns = e?.Status?.IpInfo?.DNS; + string version = e?.Status?.ServiceVersion.Version; + string op = e?.Op; + Logger.Info($"Operation {op}. running dns: {dns} at version {version}"); + + SetLogLevel(e.Status.LogLevel); + } + + private void disableHealthCheck() { + if (zetHealthcheck.Enabled) { + zetHealthcheck.Enabled = true; + zetHealthcheck.Stop(); + Logger.Info("ziti-edge-tunnel health check disabled"); + } else { + Logger.Info("ziti-edge-tunnel health check already disabled"); + } + } + + private void enableHealthCheck() { + if (!zetHealthcheck.Enabled) { + zetHealthcheck.Enabled = false; + zetHealthcheck.Start(); + Logger.Info("ziti-edge-tunnel health check enabled"); + } else { + Logger.Info("ziti-edge-tunnel health check already enabled"); + } + } + + private void Svc_OnClientConnected(object sender, object e) { + Logger.Info("successfully connected to service"); + enableHealthCheck(); + } + + private void Svc_OnClientDisconnected(object sender, object e) { + disableHealthCheck(); //no need to healthcheck when we know it's disconnected + DataClient svc = (DataClient)sender; + if (svc.ExpectedShutdown) { + //then this is fine and expected - the service is shutting down + Logger.Info("client disconnected due to clean service shutdown"); + } else { + Logger.Error("SERVICE IS DOWN and did not exit cleanly."); + + MonitorServiceStatusEvent status = new MonitorServiceStatusEvent() { + Code = 10, + Error = "SERVICE DOWN", + Message = "SERVICE DOWN", + Type = "Status", + Status = ServiceActions.ServiceStatus(), + ReleaseStream = IsBeta ? "beta" : "stable", + AutomaticUpgradeDisabled = CurrentSettings.AutomaticUpdatesDisabled.ToString(), + AutomaticUpgradeURL = CurrentSettings.AutomaticUpdateURL, + }; + EventRegistry.SendEventToConsumers(status); + } + } + + private static void EnumerateDNS() { + var ps = System.Management.Automation.PowerShell.Create(); + ps.AddScript("Get-DnsClientServerAddress"); + var results = ps.Invoke(); + + using (StringWriter sw = new StringWriter()) { + foreach (var r in results) { + string name = (string)r.Properties["InterfaceAlias"].Value; + string[] dnses = (string[])r.Properties["ServerAddresses"].Value; + sw.WriteLine($"Interface: {name}. DNS: {string.Join(",", dnses)}"); + } + Logger.Info("DNS RESULTS:\n{0}", sw.ToString()); + } + } + + private TimeSpan InstallationReminder() { + var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationReminder"); + var reminderInt = TimeSpan.Zero; + if (!TimeSpan.TryParse(installationReminderIntervalStr, out reminderInt)) { + reminderInt = new TimeSpan(0, 1, 0); + } + return reminderInt; + } + + private DateTime InstallDateFromPublishDate(DateTime publishDate) { + var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); + var instCritTimespan = TimeSpan.Zero; + if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { + instCritTimespan = TimeSpan.Parse("7:0:0:0"); + } + return publishDate + instCritTimespan; + } + + private bool InstallationIsCritical(DateTime publishDate) { + var installationReminderIntervalStr = ConfigurationManager.AppSettings.Get("InstallationCritical"); + var instCritTimespan = TimeSpan.Zero; + if (!TimeSpan.TryParse(installationReminderIntervalStr, out instCritTimespan)) { + instCritTimespan = TimeSpan.Parse("7:0:0:0"); + } + return DateTime.Now > publishDate + instCritTimespan; + } + + private void NotifyInstallationUpdates(InstallationNotificationEvent evt) { + NotifyInstallationUpdates(evt, false); + } + + private void NotifyInstallationUpdates(InstallationNotificationEvent evt, bool force) { + try { + evt.Message = "InstallationUpdate"; + evt.Type = "Notification"; + EventRegistry.SendEventToConsumers(evt); + Logger.Debug("NotifyInstallationUpdates: sent for version {0} is sent to the events pipe...", evt.ZDEVersion); + return; + } catch (Exception e) { + Logger.Error("The notification for the installation updates for version {0} has failed: {1}", evt.ZDEVersion, e); + } + } + } +} diff --git a/build-test-release.ps1 b/build-test-release.ps1 index 7b6d96dec..150fe63dc 100644 --- a/build-test-release.ps1 +++ b/build-test-release.ps1 @@ -94,4 +94,4 @@ Write-Host "" Write-Host " python -m http.server 8000" Write-Host "" Write-Host "Set the automatic upgrade url to http://localhost:8000/release-streams/local.json" -Write-Host "" \ No newline at end of file +Write-Host "" diff --git a/release-streams/beta.json b/release-streams/beta.json index 5f6736c1a..b868976e1 100644 --- a/release-streams/beta.json +++ b/release-streams/beta.json @@ -1,12 +1,12 @@ { - "name": "2.5.0.0", - "tag_name": "2.5.0.0", - "published_at": "2024-10-18T06:35:33Z", + "name": "2.5.0.13", + "tag_name": "2.5.0.13", + "published_at": "2024-10-18T08:32:49Z", "installation_critical": false, "assets": [ { - "name": "Ziti.Desktop.Edge.Client-2.5.0.0.exe", - "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.5.0.0/Ziti.Desktop.Edge.Client-2.5.0.0.exe" + "name": "Ziti.Desktop.Edge.Client-2.5.0.13.exe", + "browser_download_url": "https://github.com/openziti/desktop-edge-win/releases/download/2.5.0.13/Ziti.Desktop.Edge.Client-2.5.0.13.exe" } ] } From c95cfb6286a0b778c022388f057c44769d704c6f Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:44:53 -0400 Subject: [PATCH 12/12] update release note --- ZitiDesktopEdge.Client/DataStructures/DataStructures.cs | 1 + ZitiDesktopEdge.Client/ServiceClient/DataClient.cs | 3 ++- release-notes.md | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs index 6d551706d..0c3efd3e5 100644 --- a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs +++ b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs @@ -239,6 +239,7 @@ public class IdentifierPayload { public class EnrollIdentifierPayload { public string JwtFileName { get; set; } public string JwtContent { get; set; } + public bool UseKeychain { get; set; } } public class EnrollIdentifierFunction : ServiceFunction { diff --git a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs index 032478f04..48e8df1f1 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs @@ -159,7 +159,8 @@ async public Task AddIdentityAsync(string jwtFileName, bool activate, Command = "AddIdentity", Data = new EnrollIdentifierPayload() { JwtFileName = jwtFileName, - JwtContent = jwtContent + JwtContent = jwtContent, + // future use UseKeychain = true, } }; diff --git a/release-notes.md b/release-notes.md index e1fc303c2..7ff5b52a7 100644 --- a/release-notes.md +++ b/release-notes.md @@ -18,7 +18,8 @@ # Release 2.5.0.12 ## What's New -* Keychain integration and TPM now used! +* OIDC enabled, implementation (coming soon) +* Keychain integration and TPM enablemed, implementation (coming soon) ## Other changes: * n/a