├── .github
└── workflows
│ ├── build.yml
│ └── qodana_code_quality.yml
├── .gitignore
├── .idea
├── .gitignore
├── .idea.OpenSSHA-GUI
│ └── .idea
│ │ ├── .name
│ │ ├── projectSettingsUpdater.xml
│ │ └── workspace.xml
├── .idea.OpenSSH_GUI
│ └── .idea
│ │ ├── avalonia.xml
│ │ ├── dataSources.local.xml
│ │ ├── dataSources.xml
│ │ ├── dataSources
│ │ ├── d6fa5daa-3f37-4b1d-80de-189ee8b27c70.corrupted.20241119-114550.reason.txt
│ │ ├── d6fa5daa-3f37-4b1d-80de-189ee8b27c70.corrupted.20241119-114550.xml
│ │ ├── d6fa5daa-3f37-4b1d-80de-189ee8b27c70.xml
│ │ └── d6fa5daa-3f37-4b1d-80de-189ee8b27c70
│ │ │ └── storage_v2
│ │ │ └── _src_
│ │ │ └── schema
│ │ │ └── main.uQUzAA.meta
│ │ ├── deployment.xml
│ │ ├── git_toolbox_blame.xml
│ │ ├── git_toolbox_prj.xml
│ │ ├── indexLayout.xml
│ │ ├── projectSettingsUpdater.xml
│ │ ├── shelf
│ │ ├── Uncommitted_changes_before_Merge_at_12_06_2024_12_45_[Changes]
│ │ │ └── shelved.patch
│ │ ├── Uncommitted_changes_before_Merge_at_12_06_2024_12_45__Changes_.xml
│ │ ├── Uncommitted_changes_before_Update_at_11_06_2024_10_09_[Changes]
│ │ │ └── shelved.patch
│ │ ├── Uncommitted_changes_before_Update_at_11_06_2024_10_09__Changes_.xml
│ │ ├── Uncommitted_changes_before_Update_at_21_05_2024_08_03_[Changes]
│ │ │ └── shelved.patch
│ │ ├── Uncommitted_changes_before_Update_at_21_05_2024_08_03__Changes_.xml
│ │ └── Uncommitted_changes_before_rebase_[Changes]
│ │ │ ├── AddKeyWindow.png
│ │ │ ├── AppSettings.png
│ │ │ ├── ConnectToServerQuickConnect.png
│ │ │ ├── ConnectToServerWindowWithKey.png
│ │ │ ├── FoundPasswordProtectedKey.png
│ │ │ ├── MainWindow.png
│ │ │ ├── NewMainUI.png
│ │ │ ├── ProvidePasswordPrompt.png
│ │ │ ├── SettingsContextMenu.png
│ │ │ ├── ShowForgetPws.png
│ │ │ └── Sorted.png
│ │ ├── vcs.xml
│ │ └── workspace.xml
├── avalonia.xml
├── deployment.xml
├── encodings.xml
├── git_toolbox_prj.xml
├── indexLayout.xml
├── misc.xml
├── riderPublish.xml
└── vcs.xml
├── LICENSE
├── OpenSSHA-GUI.sln.DotSettings.user
├── OpenSSH_GUI.Core
├── Database
│ ├── Context
│ │ └── OpenSshGuiDbContext.cs
│ ├── DTO
│ │ ├── ConnectionCredentialsDto.cs
│ │ └── SshKeyDto.cs
│ └── Migrations
│ │ ├── 20240521113602_Initial.Designer.cs
│ │ ├── 20240521113602_Initial.cs
│ │ └── OpenSshGuiDbContextModelSnapshot.cs
├── Enums
│ ├── AuthType.cs
│ ├── EncryptionType.cs
│ ├── KeyType.cs
│ ├── SshConfigFiles.cs
│ └── UnixPermissions.cs
├── Extensions
│ ├── FileStreamExtensions.cs
│ ├── IConnectionCredentialsExtension.cs
│ ├── IPrivateKeySourceExtensions.cs
│ ├── KeyTypeExtension.cs
│ ├── SshConfigFilesExtension.cs
│ ├── SshKeyFormatExtension.cs
│ ├── SshKeyGenerateParamsExtensions.cs
│ └── StringExtensions.cs
├── Interfaces
│ ├── AuthorizedKeys
│ │ ├── IAuthorizedKey.cs
│ │ └── IAuthorizedKeysFile.cs
│ ├── Credentials
│ │ ├── IConnectionCredentials.cs
│ │ ├── IKeyConnectionCredentials.cs
│ │ ├── IMultiKeyConnectionCredentials.cs
│ │ └── IPasswordConnectionCredentials.cs
│ ├── Keys
│ │ ├── IPpkKey.cs
│ │ ├── ISshKey.cs
│ │ ├── ISshKeyType.cs
│ │ ├── ISshPrivateKey.cs
│ │ └── ISshPublicKey.cs
│ ├── KnownHosts
│ │ ├── IKnownHost.cs
│ │ ├── IKnownHostKey.cs
│ │ └── IKnownHostsFile.cs
│ └── Misc
│ │ ├── IKeyBase.cs
│ │ └── IServerConnection.cs
├── Lib
│ ├── Abstract
│ │ └── KeyBase.cs
│ ├── AuthorizedKeys
│ │ ├── AuthorizedKey.cs
│ │ └── AuthorizedKeysFile.cs
│ ├── Credentials
│ │ ├── ConnectionCredentials.cs
│ │ ├── KeyConnectionCredentials.cs
│ │ ├── MultiKeyConnectionCredentials.cs
│ │ └── PasswordConnectionCredentials.cs
│ ├── Keys
│ │ ├── PpkKey.cs
│ │ ├── SSHKey.cs
│ │ ├── SSHKeyType.cs
│ │ ├── SshPrivateKey.cs
│ │ └── SshPublicKey.cs
│ ├── KnownHosts
│ │ ├── KnownHost.cs
│ │ ├── KnownHostKey.cs
│ │ └── KnownHostsFile.cs
│ ├── Misc
│ │ ├── DirectoryCrawler.cs
│ │ ├── ServerConnection.cs
│ │ └── SshKeyGenerateParams.cs
│ ├── Settings
│ │ └── Settings.cs
│ └── Static
│ │ ├── FileOperations.cs
│ │ └── KeyFactory.cs
└── OpenSSH_GUI.Core.csproj
├── OpenSSH_GUI.Tests
├── OpenSSH_GUI.Tests.csproj
└── UnitTest1.cs
├── OpenSSH_GUI.sln
├── OpenSSH_GUI.sln.DotSettings
├── OpenSSH_GUI.sln.DotSettings.user
├── OpenSSH_GUI
├── App.axaml
├── App.axaml.cs
├── Assets
│ ├── appicon.ico
│ ├── appicon.png
│ └── avalonia-logo.ico
├── Converters
│ ├── PlatformIdConverter.cs
│ ├── SingleSshKeyTypeConverter.cs
│ └── SshKeyTypeConverter.cs
├── OpenSSH_GUI.csproj
├── Program.cs
├── Resources
│ ├── Controls
│ │ ├── BottomButtons.axaml
│ │ └── BottomButtons.axaml.cs
│ ├── Styles
│ │ └── ISshKeyStyles.axaml
│ └── Wrapper
│ │ ├── WindowBase.cs
│ │ └── WindowInteraction.cs
├── StringsAndTexts.Designer.cs
├── StringsAndTexts.de.resx
├── StringsAndTexts.resx
├── ViewLocator.cs
├── ViewModels
│ ├── AddKeyWindowViewModel.cs
│ ├── ApplicationSettingsViewModel.cs
│ ├── ConnectToServerViewModel.cs
│ ├── EditAuthorizedKeysViewModel.cs
│ ├── EditKnownHostsViewModel.cs
│ ├── EditSavedServerEntryViewModel.cs
│ ├── ExportWindowViewModel.cs
│ ├── MainWindowViewModel.cs
│ └── ViewModelBase.cs
├── Views
│ ├── AddKeyWindow.axaml
│ ├── AddKeyWindow.axaml.cs
│ ├── ApplicationSettingsWindow.axaml
│ ├── ApplicationSettingsWindow.axaml.cs
│ ├── ConnectToServerWindow.axaml
│ ├── ConnectToServerWindow.axaml.cs
│ ├── EditAuthorizedKeysWindow.axaml
│ ├── EditAuthorizedKeysWindow.axaml.cs
│ ├── EditKnownHostsWindow.axaml
│ ├── EditKnownHostsWindow.axaml.cs
│ ├── EditSavedServerEntry.axaml
│ ├── EditSavedServerEntry.axaml.cs
│ ├── ExportWindow.axaml
│ ├── ExportWindow.axaml.cs
│ ├── MainWindow.axaml
│ └── MainWindow.axaml.cs
└── app.manifest
├── README.md
├── images
├── AddKeyWindow.png
├── AppSettings.png
├── ConnectToServerQuickConnect.png
├── ConnectToServerWindow.png
├── ConnectToServerWindowSuccess.png
├── ConnectToServerWindowWithKey.png
├── EditAuthorizedKeysWindow.png
├── EditAuthorizedKeysWindowRemote.png
├── ExportKeyWindow.png
├── FoundPasswordProtectedKey.png
├── KnownHostsWindow.png
├── MainWindow.png
├── NewMainUI.png
├── ProvidePasswordPrompt.png
├── SettingsContextMenu.png
├── ShowForgetPws.png
├── Sorted.png
├── tooltip.png
├── tooltipKey.png
└── tooltipServer.png
├── nuget.config
└── qodana.yaml
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build and Release
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | push:
7 | tags:
8 | - '*'
9 |
10 | jobs:
11 | build_and_release:
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - name: Checkout repository
16 | uses: actions/checkout@v2
17 |
18 | - name: Cache dependencies
19 | uses: actions/cache@v2
20 | with:
21 | path: |
22 | ~/.nuget/packages
23 | ~/.dotnet
24 | key: ${{ runner.os }}-dotnet-${{ hashFiles('**/*.csproj') }}
25 | restore-keys: |
26 | ${{ runner.os }}-dotnet-
27 |
28 | - name: Setup .NET SDK
29 | uses: actions/setup-dotnet@v4
30 | with:
31 | dotnet-version: 8.0.x
32 |
33 | - name: Build for Linux
34 | run: dotnet publish -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true OpenSSH_GUI/OpenSSH_GUI.csproj
35 |
36 | - name: Build for Windows
37 | run: dotnet publish -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true OpenSSH_GUI/OpenSSH_GUI.csproj
38 |
39 | - name: Build for macOS
40 | run: dotnet publish -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true OpenSSH_GUI/OpenSSH_GUI.csproj
41 |
42 | - name: Create Release
43 | id: create_release
44 | uses: actions/create-release@v1
45 | env:
46 | GITHUB_TOKEN: ${{ secrets.TOKEN }}
47 | with:
48 | tag_name: ${{ github.ref }}
49 | release_name: Release ${{ github.ref }}
50 | draft: false
51 | prerelease: false
52 |
53 | - name: Upload Linux release to Release
54 | uses: actions/upload-release-asset@v1
55 | env:
56 | GITHUB_TOKEN: ${{ secrets.TOKEN }}
57 | with:
58 | upload_url: ${{ steps.create_release.outputs.upload_url }}
59 | asset_path: OpenSSH_GUI/bin/Release/net8.0/linux-x64/publish/OpenSSH_GUI
60 | asset_name: OpenSSH-GUI-linux-x64.bin
61 | asset_content_type: application/x-openssh-gui
62 |
63 | - name: Upload Windows release to Release
64 | uses: actions/upload-release-asset@v1
65 | env:
66 | GITHUB_TOKEN: ${{ secrets.TOKEN }}
67 | with:
68 | upload_url: ${{ steps.create_release.outputs.upload_url }}
69 | asset_path: OpenSSH_GUI/bin/Release/net8.0/win-x64/publish/OpenSSH_GUI.exe
70 | asset_name: OpenSSH-GUI-win-x64.exe
71 | asset_content_type: application/x-openssh-gui
72 |
73 | - name: Upload macOS release to Release
74 | uses: actions/upload-release-asset@v1
75 | env:
76 | GITHUB_TOKEN: ${{ secrets.TOKEN }}
77 | with:
78 | upload_url: ${{ steps.create_release.outputs.upload_url }}
79 | asset_path: OpenSSH_GUI/bin/Release/net8.0/osx-x64/publish/OpenSSH_GUI
80 | asset_name: OpenSSH-GUI-osx-x64.bin
81 | asset_content_type: application/x-openssh-gui
82 |
--------------------------------------------------------------------------------
/.github/workflows/qodana_code_quality.yml:
--------------------------------------------------------------------------------
1 | name: Qodana
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | push:
6 | branches: # Specify your branches here
7 | - main # The 'main' branch
8 | - 'releases/*' # The release branches
9 |
10 | jobs:
11 | qodana:
12 | runs-on: ubuntu-latest
13 | permissions:
14 | contents: write
15 | pull-requests: write
16 | checks: write
17 | steps:
18 | - uses: actions/checkout@v3
19 | with:
20 | ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
21 | fetch-depth: 0 # a full history is required for pull request analysis
22 | - name: 'Qodana Scan'
23 | uses: JetBrains/qodana-action@v2024.1
24 | with:
25 | pr-mode: false
26 | env:
27 | QODANA_TOKEN: ${{ secrets.QODANA_TOKEN_1539972718 }}
28 | QODANA_ENDPOINT: 'https://qodana.cloud'
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | /packages/
4 | riderModule.iml
5 | /_ReSharper.Caches/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /contentModel.xml
6 | /projectSettingsUpdater.xml
7 | /.idea.OpenSSH_GUI.iml
8 | /modules.xml
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 | # Datasource local storage ignored files
12 | /dataSources/
13 | /dataSources.local.xml
14 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSHA-GUI/.idea/.name:
--------------------------------------------------------------------------------
1 | OpenSSHA-GUI
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSHA-GUI/.idea/projectSettingsUpdater.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/avalonia.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/dataSources.local.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "
7 |
8 |
9 | no-auth
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/dataSources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sqlite.xerial
6 | true
7 | org.sqlite.JDBC
8 | jdbc:sqlite:$USER_HOME$/AppData/Roaming/OpenSSH_GUI/OpenSSH_GUI
9 |
10 |
11 |
12 | $ProjectFileDir$
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/dataSources/d6fa5daa-3f37-4b1d-80de-189ee8b27c70.corrupted.20241119-114550.reason.txt:
--------------------------------------------------------------------------------
1 | com.thoughtworks.xstream.io.StreamException:
2 | at com.thoughtworks.xstream.io.xml.XppReader.pullNextEvent(XppReader.java:124)
3 | at com.thoughtworks.xstream.io.xml.AbstractPullReader.readRealEvent(AbstractPullReader.java:148)
4 | at com.thoughtworks.xstream.io.xml.AbstractPullReader.readEvent(AbstractPullReader.java:135)
5 | at com.thoughtworks.xstream.io.xml.AbstractPullReader.hasMoreChildren(AbstractPullReader.java:87)
6 | at com.intellij.database.model.serialization.FixHierarchicalStreamReader.hasMoreChildren(FixHierarchicalStream.kt)
7 | at com.intellij.database.model.serialization.ModelImporter.deserializeModelHierarchy(ModelImporter.java:147)
8 | at com.intellij.database.model.serialization.ModelImporter.deserialize(ModelImporter.java:76)
9 | at com.intellij.database.dataSource.DataSourceModelStorageImpl$Companion.readModel(DataSourceModelStorageImpl.kt:605)
10 | at com.intellij.database.dataSource.DataSourceModelStorageImpl$Companion.readModel(DataSourceModelStorageImpl.kt:588)
11 | at com.intellij.database.dataSource.DataSourceModelStorageImpl.readModel(DataSourceModelStorageImpl.kt:373)
12 | at com.intellij.database.dataSource.DataSourceModelStorageImpl.loadModels(DataSourceModelStorageImpl.kt:262)
13 | at com.intellij.database.dataSource.DataSourceModelStorageImpl.readStateHeavy(DataSourceModelStorageImpl.kt:161)
14 | at com.intellij.database.dataSource.DataSourceModelStorageImpl.continueLoadingWhenInitialized$lambda$8$lambda$7$lambda$6$lambda$5(DataSourceModelStorageImpl.kt:144)
15 | at com.intellij.openapi.progress.util.BackgroundTaskUtil.lambda$runUnderDisposeAwareIndicator$15(BackgroundTaskUtil.java:371)
16 | at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$1(CoreProgressManager.java:223)
17 | at com.intellij.platform.diagnostic.telemetry.helpers.TraceKt.use(trace.kt:45)
18 | at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:222)
19 | at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$14(CoreProgressManager.java:674)
20 | at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:749)
21 | at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:705)
22 | at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:673)
23 | at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:79)
24 | at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:203)
25 | at com.intellij.openapi.progress.util.BackgroundTaskUtil.runUnderDisposeAwareIndicator(BackgroundTaskUtil.java:366)
26 | at com.intellij.openapi.progress.util.BackgroundTaskUtil.runUnderDisposeAwareIndicator(BackgroundTaskUtil.java:349)
27 | at com.intellij.database.dataSource.DataSourceModelStorageImpl.continueLoadingWhenInitialized$lambda$8$lambda$7$lambda$6(DataSourceModelStorageImpl.kt:143)
28 | at com.intellij.database.util.AsyncTask$Frame$compute$$inlined$supply$1$1.run(AsyncTask.kt:338)
29 | at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$14(CoreProgressManager.java:674)
30 | at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:749)
31 | at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:705)
32 | at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:673)
33 | at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:79)
34 | at com.intellij.database.util.AsyncTask$Frame$compute$$inlined$supply$1.get(AsyncTask.kt:900)
35 | at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
36 | at com.intellij.util.concurrency.ChildContext$runInChildContext$1.invoke(propagation.kt:101)
37 | at com.intellij.util.concurrency.ChildContext$runInChildContext$1.invoke(propagation.kt:101)
38 | at com.intellij.util.concurrency.ChildContext.runInChildContext(propagation.kt:107)
39 | at com.intellij.util.concurrency.ChildContext.runInChildContext(propagation.kt:101)
40 | at com.intellij.util.concurrency.ContextRunnable.run(ContextRunnable.java:27)
41 | at com.intellij.database.dataSource.DataSourceModelStorageImpl$continueLoadingWhenInitialized$2$1$1$1$1.invokeSuspend(DataSourceModelStorageImpl.kt:142)
42 | at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
43 | at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
44 | at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:608)
45 | at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:873)
46 | at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:763)
47 | at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:750)
48 | Caused by: org.xmlpull.v1.XmlPullParserException: unexpected character in markup < (position: END_TAG seen ...\r\n \r\n<<... @7:3)
49 | at io.github.xstream.mxparser.MXParser.nextImpl(MXParser.java:1273)
50 | at io.github.xstream.mxparser.MXParser.next(MXParser.java:1104)
51 | at com.thoughtworks.xstream.io.xml.XppReader.pullNextEvent(XppReader.java:109)
52 | ... 45 more
53 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/dataSources/d6fa5daa-3f37-4b1d-80de-189ee8b27c70/storage_v2/_src_/schema/main.uQUzAA.meta:
--------------------------------------------------------------------------------
1 | #n:main
2 | ! [0, 0, null, null, -2147483648, -2147483648]
3 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/deployment.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/git_toolbox_blame.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/git_toolbox_prj.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/projectSettingsUpdater.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_Merge_at_12_06_2024_12_45__Changes_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_Update_at_11_06_2024_10_09__Changes_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_Update_at_21_05_2024_08_03__Changes_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/AddKeyWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/AddKeyWindow.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/AppSettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/AppSettings.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ConnectToServerQuickConnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ConnectToServerQuickConnect.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ConnectToServerWindowWithKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ConnectToServerWindowWithKey.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/FoundPasswordProtectedKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/FoundPasswordProtectedKey.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/MainWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/MainWindow.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/NewMainUI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/NewMainUI.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ProvidePasswordPrompt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ProvidePasswordPrompt.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/SettingsContextMenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/SettingsContextMenu.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ShowForgetPws.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/ShowForgetPws.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/Sorted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/.idea/.idea.OpenSSH_GUI/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/Sorted.png
--------------------------------------------------------------------------------
/.idea/.idea.OpenSSH_GUI/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/avalonia.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/deployment.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/git_toolbox_prj.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/riderPublish.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Oliver Schantz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/OpenSSHA-GUI.sln.DotSettings.user:
--------------------------------------------------------------------------------
1 |
2 | <SessionState ContinuousTestingMode="0" IsActive="True" Name="TestCommand" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
3 | <TestAncestor>
4 | <TestId>NUnit3x::2CFEA7E7-DA50-446C-8934-210FB4342B77::net8.0::OpenSSHA_GUITests.Tests.Shrug</TestId>
5 | </TestAncestor>
6 | </SessionState>
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Database/Context/OpenSshGuiDbContext.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 17.05.2024 - 08:05:28
3 | // Last edit: 17.05.2024 - 08:05:29
4 |
5 | using Microsoft.Data.Sqlite;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.EntityFrameworkCore.DataEncryption;
8 | using Microsoft.EntityFrameworkCore.DataEncryption.Providers;
9 | using OpenSSH_GUI.Core.Database.DTO;
10 | using OpenSSH_GUI.Core.Lib.Settings;
11 |
12 | namespace OpenSSH_GUI.Core.Database.Context;
13 |
14 | ///
15 | /// Represents the database context for the OpenSSH GUI application.
16 | ///
17 | public class OpenSshGuiDbContext : DbContext
18 | {
19 | ///
20 | /// Represents the initialization vector (IV) used for data encryption in the OpenSshGuiDbContext class.
21 | ///
22 | private readonly byte[] _encyptionIV = Convert.FromBase64String("lWXnTKuRPnieE8nzpyl1Gg==");
23 |
24 | ///
25 | /// Represents the encryption key used for data encryption in the OpenSshGuiDbContext class.
26 | ///
27 | private readonly byte[] _encyptionKey = Convert.FromBase64String("Jk7JqD9mAafdvTvhNESHkXBFdy7phfyDR0FsnyGw2nY=");
28 |
29 | ///
30 | /// Represents an encryption provider used by the OpenSshGuiDbContext.
31 | ///
32 | private IEncryptionProvider _provider => new AesProvider(_encyptionKey, _encyptionIV);
33 |
34 |
35 | ///
36 | /// Represents a settings file for the application.
37 | ///
38 | public virtual DbSet Settings { get; set; }
39 |
40 | ///
41 | /// Represents a SSH key data transfer object (DTO).
42 | ///
43 | public virtual DbSet KeyDtos { get; set; }
44 |
45 | ///
46 | /// Represents a data transfer object (DTO) for connection credentials.
47 | ///
48 | public virtual DbSet ConnectionCredentialsDtos { get; set; }
49 |
50 | ///
51 | /// Configures the database connection for the OpenSshGuiDbContext.
52 | ///
53 | /// The options builder used to configure the database connection.
54 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
55 | {
56 | var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
57 | AppDomain.CurrentDomain.FriendlyName);
58 | if (!Directory.Exists(appDataPath)) Directory.CreateDirectory(appDataPath);
59 | var dbFilePath = Path.Combine(appDataPath, $"{AppDomain.CurrentDomain.FriendlyName}");
60 | var builder = new SqliteConnectionStringBuilder
61 | {
62 | DataSource = dbFilePath,
63 | Mode = SqliteOpenMode.ReadWriteCreate,
64 | Pooling = true
65 | };
66 | optionsBuilder.UseSqlite(builder.ToString())
67 | .UseLazyLoadingProxies();
68 | base.OnConfiguring(optionsBuilder);
69 | }
70 |
71 | ///
72 | /// This method is called when the model for a derived context has been initialized, but before
73 | /// the model has been locked down and used to initialize the context. It allows further
74 | /// modification of the model before it is locked down.
75 | ///
76 | /// The builder that defines the model for the context.
77 | protected override void OnModelCreating(ModelBuilder modelBuilder)
78 | {
79 | modelBuilder.UseEncryption(_provider);
80 |
81 | modelBuilder.Entity().HasKey(e => e.Version);
82 |
83 | modelBuilder.Entity().HasKey(e => e.Id);
84 | modelBuilder.Entity()
85 | .HasIndex(e => e.AbsolutePath).IsUnique();
86 |
87 | modelBuilder.Entity()
88 | .HasMany(e => e.ConnectionCredentialsDto)
89 | .WithMany(e => e.KeyDtos);
90 |
91 | modelBuilder.Entity().HasKey(e => e.Id);
92 | modelBuilder.Entity()
93 | .HasMany(e => e.KeyDtos)
94 | .WithMany(e => e.ConnectionCredentialsDto);
95 | }
96 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Database/DTO/ConnectionCredentialsDto.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 18.05.2024 - 16:05:59
3 | // Last edit: 18.05.2024 - 16:05:59
4 |
5 | using System.Collections.ObjectModel;
6 | using System.ComponentModel.DataAnnotations;
7 | using OpenSSH_GUI.Core.Enums;
8 | using OpenSSH_GUI.Core.Interfaces.Credentials;
9 | using OpenSSH_GUI.Core.Interfaces.Keys;
10 | using OpenSSH_GUI.Core.Lib.Credentials;
11 |
12 | namespace OpenSSH_GUI.Core.Database.DTO;
13 |
14 | ///
15 | /// Represents a data transfer object (DTO) for connection credentials.
16 | ///
17 | public class ConnectionCredentialsDto
18 | {
19 | ///
20 | /// Represents the property Id of a connection credential.
21 | ///
22 | [Key]
23 | public int Id { get; set; }
24 |
25 | ///
26 | /// Represents the hostname of a connection.
27 | ///
28 | [Encrypted]
29 | public string Hostname { get; set; }
30 |
31 | ///
32 | /// Represents the username used for connection credentials.
33 | ///
34 | [Encrypted]
35 | public string Username { get; set; }
36 |
37 | ///
38 | /// The port number for the SSH connection.
39 | ///
40 | public int Port { get; set; }
41 |
42 | ///
43 | /// Represents the authentication type for a connection.
44 | ///
45 | public AuthType AuthType { get; set; }
46 |
47 | ///
48 | /// Represents a data transfer object for connection credentials.
49 | ///
50 | public virtual List KeyDtos { get; set; }
51 |
52 | ///
53 | /// Represents the password for a connection.
54 | ///
55 | [Encrypted]
56 | public string? Password { get; set; }
57 |
58 | ///
59 | /// Gets or sets a flag indicating whether the password is encrypted.
60 | ///
61 | public bool PasswordEncrypted { get; set; }
62 |
63 | ///
64 | /// Converts a ConnectionCredentialsDto object to an instance of IConnectionCredentials.
65 | ///
66 | /// The converted IConnectionCredentials object.
67 | public IConnectionCredentials ToCredentials()
68 | {
69 | var g = KeyDtos.Select(e => e.ToKey());
70 | return ToCredentials(ref g);
71 | }
72 |
73 | public IConnectionCredentials ToCredentials(ref ObservableCollection keys)
74 | {
75 | var g = keys.Select(e => e);
76 | return ToCredentials(ref g);
77 | }
78 |
79 | public IConnectionCredentials ToCredentials(ref IEnumerable keys)
80 | {
81 | return AuthType switch
82 | {
83 | AuthType.Key => new KeyConnectionCredentials(Hostname, Username, KeyDtos.First().ToKey()) { Id = Id },
84 | AuthType.Password => new PasswordConnectionCredentials(Hostname, Username, Password,
85 | PasswordEncrypted) { Id = Id },
86 | AuthType.MultiKey => new MultiKeyConnectionCredentials(Hostname, Username,
87 | keys.Where(e => KeyDtos.All(f => f.Id != e.Id))) { Id = Id }
88 | };
89 | }
90 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Database/DTO/SshKeyDto.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 17.05.2024 - 08:05:19
3 | // Last edit: 17.05.2024 - 08:05:19
4 |
5 | using System.ComponentModel.DataAnnotations;
6 | using OpenSSH_GUI.Core.Interfaces.Keys;
7 | using OpenSSH_GUI.Core.Lib.Static;
8 | using SshNet.Keygen;
9 |
10 | namespace OpenSSH_GUI.Core.Database.DTO;
11 |
12 | ///
13 | /// Represents a SSH key data transfer object (DTO).
14 | ///
15 | public class SshKeyDto
16 | {
17 | ///
18 | /// Gets or sets the ID of the SSH key.
19 | ///
20 | ///
21 | /// The ID uniquely identifies the SSH key.
22 | ///
23 | [Key]
24 | public int Id { get; set; }
25 |
26 | ///
27 | /// Gets or sets the absolute path of the SSH key.
28 | ///
29 | public string AbsolutePath { get; set; }
30 |
31 | ///
32 | /// Represents the format of an SSH key.
33 | ///
34 | public SshKeyFormat Format { get; set; }
35 |
36 | ///
37 | /// Represents a password associated with a specific SSH key.
38 | ///
39 | [Encrypted]
40 | public string? Password { get; set; }
41 |
42 | ///
43 | /// Represents the connection credentials for SSH connection.
44 | ///
45 | public virtual IEnumerable ConnectionCredentialsDto { get; set; }
46 |
47 | ///
48 | /// Converts an instance of to an instance of .
49 | ///
50 | /// The converted instance.
51 | public ISshKey? ToKey()
52 | {
53 | return KeyFactory.FromPath(AbsolutePath, Password, Id);
54 | }
55 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Database/Migrations/20240521113602_Initial.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Migrations;
5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
6 | using OpenSSH_GUI.Core.Database.Context;
7 |
8 | #nullable disable
9 |
10 | namespace OpenSSH_GUI.Core.Database.Migrations
11 | {
12 | [DbContext(typeof(OpenSshGuiDbContext))]
13 | [Migration("20240521113602_Initial")]
14 | partial class Initial
15 | {
16 | ///
17 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
18 | {
19 | #pragma warning disable 612, 618
20 | modelBuilder
21 | .HasAnnotation("ProductVersion", "8.0.5")
22 | .HasAnnotation("Proxies:ChangeTracking", false)
23 | .HasAnnotation("Proxies:CheckEquality", false)
24 | .HasAnnotation("Proxies:LazyLoading", true);
25 |
26 | modelBuilder.Entity("ConnectionCredentialsDtoSshKeyDto", b =>
27 | {
28 | b.Property("ConnectionCredentialsDtoId")
29 | .HasColumnType("INTEGER");
30 |
31 | b.Property("KeyDtosId")
32 | .HasColumnType("INTEGER");
33 |
34 | b.HasKey("ConnectionCredentialsDtoId", "KeyDtosId");
35 |
36 | b.HasIndex("KeyDtosId");
37 |
38 | b.ToTable("ConnectionCredentialsDtoSshKeyDto");
39 | });
40 |
41 | modelBuilder.Entity("OpenSSH_GUI.Core.Database.DTO.ConnectionCredentialsDto", b =>
42 | {
43 | b.Property("Id")
44 | .ValueGeneratedOnAdd()
45 | .HasColumnType("INTEGER");
46 |
47 | b.Property("AuthType")
48 | .HasColumnType("INTEGER");
49 |
50 | b.Property("Hostname")
51 | .IsRequired()
52 | .HasColumnType("TEXT");
53 |
54 | b.Property("Password")
55 | .HasColumnType("TEXT");
56 |
57 | b.Property("PasswordEncrypted")
58 | .HasColumnType("INTEGER");
59 |
60 | b.Property("Port")
61 | .HasColumnType("INTEGER");
62 |
63 | b.Property("Username")
64 | .IsRequired()
65 | .HasColumnType("TEXT");
66 |
67 | b.HasKey("Id");
68 |
69 | b.ToTable("ConnectionCredentialsDtos");
70 | });
71 |
72 | modelBuilder.Entity("OpenSSH_GUI.Core.Database.DTO.SshKeyDto", b =>
73 | {
74 | b.Property("Id")
75 | .ValueGeneratedOnAdd()
76 | .HasColumnType("INTEGER");
77 |
78 | b.Property("AbsolutePath")
79 | .IsRequired()
80 | .HasColumnType("TEXT");
81 |
82 | b.Property("Format")
83 | .HasColumnType("INTEGER");
84 |
85 | b.Property("Password")
86 | .HasColumnType("TEXT");
87 |
88 | b.HasKey("Id");
89 |
90 | b.HasIndex("AbsolutePath")
91 | .IsUnique();
92 |
93 | b.ToTable("KeyDtos");
94 | });
95 |
96 | modelBuilder.Entity("OpenSSH_GUI.Core.Lib.Settings.Settings", b =>
97 | {
98 | b.Property("Version")
99 | .HasColumnType("TEXT");
100 |
101 | b.Property("ConvertPpkAutomatically")
102 | .HasColumnType("INTEGER");
103 |
104 | b.HasKey("Version");
105 |
106 | b.ToTable("Settings");
107 | });
108 |
109 | modelBuilder.Entity("ConnectionCredentialsDtoSshKeyDto", b =>
110 | {
111 | b.HasOne("OpenSSH_GUI.Core.Database.DTO.ConnectionCredentialsDto", null)
112 | .WithMany()
113 | .HasForeignKey("ConnectionCredentialsDtoId")
114 | .OnDelete(DeleteBehavior.Cascade)
115 | .IsRequired();
116 |
117 | b.HasOne("OpenSSH_GUI.Core.Database.DTO.SshKeyDto", null)
118 | .WithMany()
119 | .HasForeignKey("KeyDtosId")
120 | .OnDelete(DeleteBehavior.Cascade)
121 | .IsRequired();
122 | });
123 | #pragma warning restore 612, 618
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Database/Migrations/20240521113602_Initial.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace OpenSSH_GUI.Core.Database.Migrations
6 | {
7 | ///
8 | public partial class Initial : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.CreateTable(
14 | name: "ConnectionCredentialsDtos",
15 | columns: table => new
16 | {
17 | Id = table.Column(type: "INTEGER", nullable: false)
18 | .Annotation("Sqlite:Autoincrement", true),
19 | Hostname = table.Column(type: "TEXT", nullable: false),
20 | Username = table.Column(type: "TEXT", nullable: false),
21 | Port = table.Column(type: "INTEGER", nullable: false),
22 | AuthType = table.Column(type: "INTEGER", nullable: false),
23 | Password = table.Column(type: "TEXT", nullable: true),
24 | PasswordEncrypted = table.Column(type: "INTEGER", nullable: false)
25 | },
26 | constraints: table =>
27 | {
28 | table.PrimaryKey("PK_ConnectionCredentialsDtos", x => x.Id);
29 | });
30 |
31 | migrationBuilder.CreateTable(
32 | name: "KeyDtos",
33 | columns: table => new
34 | {
35 | Id = table.Column(type: "INTEGER", nullable: false)
36 | .Annotation("Sqlite:Autoincrement", true),
37 | AbsolutePath = table.Column(type: "TEXT", nullable: false),
38 | Format = table.Column(type: "INTEGER", nullable: false),
39 | Password = table.Column(type: "TEXT", nullable: true)
40 | },
41 | constraints: table =>
42 | {
43 | table.PrimaryKey("PK_KeyDtos", x => x.Id);
44 | });
45 |
46 | migrationBuilder.CreateTable(
47 | name: "Settings",
48 | columns: table => new
49 | {
50 | Version = table.Column(type: "TEXT", nullable: false),
51 | ConvertPpkAutomatically = table.Column(type: "INTEGER", nullable: false)
52 | },
53 | constraints: table =>
54 | {
55 | table.PrimaryKey("PK_Settings", x => x.Version);
56 | });
57 |
58 | migrationBuilder.CreateTable(
59 | name: "ConnectionCredentialsDtoSshKeyDto",
60 | columns: table => new
61 | {
62 | ConnectionCredentialsDtoId = table.Column(type: "INTEGER", nullable: false),
63 | KeyDtosId = table.Column(type: "INTEGER", nullable: false)
64 | },
65 | constraints: table =>
66 | {
67 | table.PrimaryKey("PK_ConnectionCredentialsDtoSshKeyDto", x => new { x.ConnectionCredentialsDtoId, x.KeyDtosId });
68 | table.ForeignKey(
69 | name: "FK_ConnectionCredentialsDtoSshKeyDto_ConnectionCredentialsDtos_ConnectionCredentialsDtoId",
70 | column: x => x.ConnectionCredentialsDtoId,
71 | principalTable: "ConnectionCredentialsDtos",
72 | principalColumn: "Id",
73 | onDelete: ReferentialAction.Cascade);
74 | table.ForeignKey(
75 | name: "FK_ConnectionCredentialsDtoSshKeyDto_KeyDtos_KeyDtosId",
76 | column: x => x.KeyDtosId,
77 | principalTable: "KeyDtos",
78 | principalColumn: "Id",
79 | onDelete: ReferentialAction.Cascade);
80 | });
81 |
82 | migrationBuilder.CreateIndex(
83 | name: "IX_ConnectionCredentialsDtoSshKeyDto_KeyDtosId",
84 | table: "ConnectionCredentialsDtoSshKeyDto",
85 | column: "KeyDtosId");
86 |
87 | migrationBuilder.CreateIndex(
88 | name: "IX_KeyDtos_AbsolutePath",
89 | table: "KeyDtos",
90 | column: "AbsolutePath",
91 | unique: true);
92 | }
93 |
94 | ///
95 | protected override void Down(MigrationBuilder migrationBuilder)
96 | {
97 | migrationBuilder.DropTable(
98 | name: "ConnectionCredentialsDtoSshKeyDto");
99 |
100 | migrationBuilder.DropTable(
101 | name: "Settings");
102 |
103 | migrationBuilder.DropTable(
104 | name: "ConnectionCredentialsDtos");
105 |
106 | migrationBuilder.DropTable(
107 | name: "KeyDtos");
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Database/Migrations/OpenSshGuiDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
5 | using OpenSSH_GUI.Core.Database.Context;
6 |
7 | #nullable disable
8 |
9 | namespace OpenSSH_GUI.Core.Database.Migrations
10 | {
11 | [DbContext(typeof(OpenSshGuiDbContext))]
12 | partial class OpenSshGuiDbContextModelSnapshot : ModelSnapshot
13 | {
14 | protected override void BuildModel(ModelBuilder modelBuilder)
15 | {
16 | #pragma warning disable 612, 618
17 | modelBuilder
18 | .HasAnnotation("ProductVersion", "8.0.5")
19 | .HasAnnotation("Proxies:ChangeTracking", false)
20 | .HasAnnotation("Proxies:CheckEquality", false)
21 | .HasAnnotation("Proxies:LazyLoading", true);
22 |
23 | modelBuilder.Entity("ConnectionCredentialsDtoSshKeyDto", b =>
24 | {
25 | b.Property("ConnectionCredentialsDtoId")
26 | .HasColumnType("INTEGER");
27 |
28 | b.Property("KeyDtosId")
29 | .HasColumnType("INTEGER");
30 |
31 | b.HasKey("ConnectionCredentialsDtoId", "KeyDtosId");
32 |
33 | b.HasIndex("KeyDtosId");
34 |
35 | b.ToTable("ConnectionCredentialsDtoSshKeyDto");
36 | });
37 |
38 | modelBuilder.Entity("OpenSSH_GUI.Core.Database.DTO.ConnectionCredentialsDto", b =>
39 | {
40 | b.Property("Id")
41 | .ValueGeneratedOnAdd()
42 | .HasColumnType("INTEGER");
43 |
44 | b.Property("AuthType")
45 | .HasColumnType("INTEGER");
46 |
47 | b.Property("Hostname")
48 | .IsRequired()
49 | .HasColumnType("TEXT");
50 |
51 | b.Property("Password")
52 | .HasColumnType("TEXT");
53 |
54 | b.Property("PasswordEncrypted")
55 | .HasColumnType("INTEGER");
56 |
57 | b.Property("Port")
58 | .HasColumnType("INTEGER");
59 |
60 | b.Property("Username")
61 | .IsRequired()
62 | .HasColumnType("TEXT");
63 |
64 | b.HasKey("Id");
65 |
66 | b.ToTable("ConnectionCredentialsDtos");
67 | });
68 |
69 | modelBuilder.Entity("OpenSSH_GUI.Core.Database.DTO.SshKeyDto", b =>
70 | {
71 | b.Property("Id")
72 | .ValueGeneratedOnAdd()
73 | .HasColumnType("INTEGER");
74 |
75 | b.Property("AbsolutePath")
76 | .IsRequired()
77 | .HasColumnType("TEXT");
78 |
79 | b.Property("Format")
80 | .HasColumnType("INTEGER");
81 |
82 | b.Property("Password")
83 | .HasColumnType("TEXT");
84 |
85 | b.HasKey("Id");
86 |
87 | b.HasIndex("AbsolutePath")
88 | .IsUnique();
89 |
90 | b.ToTable("KeyDtos");
91 | });
92 |
93 | modelBuilder.Entity("OpenSSH_GUI.Core.Lib.Settings.Settings", b =>
94 | {
95 | b.Property("Version")
96 | .HasColumnType("TEXT");
97 |
98 | b.Property("ConvertPpkAutomatically")
99 | .HasColumnType("INTEGER");
100 |
101 | b.HasKey("Version");
102 |
103 | b.ToTable("Settings");
104 | });
105 |
106 | modelBuilder.Entity("ConnectionCredentialsDtoSshKeyDto", b =>
107 | {
108 | b.HasOne("OpenSSH_GUI.Core.Database.DTO.ConnectionCredentialsDto", null)
109 | .WithMany()
110 | .HasForeignKey("ConnectionCredentialsDtoId")
111 | .OnDelete(DeleteBehavior.Cascade)
112 | .IsRequired();
113 |
114 | b.HasOne("OpenSSH_GUI.Core.Database.DTO.SshKeyDto", null)
115 | .WithMany()
116 | .HasForeignKey("KeyDtosId")
117 | .OnDelete(DeleteBehavior.Cascade)
118 | .IsRequired();
119 | });
120 | #pragma warning restore 612, 618
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Enums/AuthType.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:24
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Enums;
10 |
11 | ///
12 | /// Represents the types of authentication supported for SSH connections.
13 | ///
14 | public enum AuthType
15 | {
16 | ///
17 | /// Represents connection credentials using password authentication.
18 | ///
19 | Password,
20 |
21 | ///
22 | /// Represents the authentication type of connection credentials using SSH key.
23 | ///
24 | Key,
25 |
26 | ///
27 | /// Represents a multi-key authentication type for SSH connections.
28 | ///
29 | MultiKey
30 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Enums/EncryptionType.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:24
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Enums;
10 |
11 | ///
12 | /// The available encryption types for SSH keys.
13 | ///
14 | public enum EncryptionType
15 | {
16 | ///
17 | /// Represents the NONE member of the EncryptionType enum.
18 | ///
19 | NONE,
20 |
21 | ///
22 | /// Represents the RSA encryption type.
23 | ///
24 | RSA,
25 |
26 | ///
27 | /// Enumeration member representing the DSA encryption type.
28 | ///
29 | DSA,
30 |
31 | ///
32 | /// Represents the encryption type for ECDSA.
33 | ///
34 | ///
35 | /// The ECDSA encryption type is used in the OpenSSH_GUI.Core library for representing ECDSA keys.
36 | ///
37 | ECDSA,
38 |
39 | ///
40 | /// The ED25519 encryption type.
41 | ///
42 | ED25519
43 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Enums/KeyType.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:24
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Enums;
10 |
11 | ///
12 | /// Enumeration for SSH key types.
13 | ///
14 | public enum KeyType
15 | {
16 | // ReSharper disable InconsistentNaming
17 | ///
18 | /// RSA key type.
19 | ///
20 | RSA,
21 |
22 | ///
23 | /// Represents the ECDSA key type.
24 | ///
25 | ECDSA,
26 |
27 | ///
28 | /// Represents the ED25519 key type.
29 | ///
30 | ED25519
31 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Enums/SshConfigFiles.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:24
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Enums;
10 |
11 | ///
12 | /// Enumeration of SSH configuration files.
13 | ///
14 | public enum SshConfigFiles
15 | {
16 | // ReSharper disable InconsistentNaming
17 | Authorized_Keys,
18 |
19 | ///
20 | /// Represents the Known_Hosts SSH config file.
21 | ///
22 | Known_Hosts,
23 |
24 | ///
25 | /// Enumerates the SSH configuration files.
26 | ///
27 | Config,
28 |
29 | ///
30 | /// Represents the Sshd_Config option of the SshConfigFiles enumeration.
31 | ///
32 | Sshd_Config
33 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Enums/UnixPermissions.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 30.05.2024 - 11:05:16
5 | // Last edit: 30.05.2024 - 11:05:17
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Enums;
10 |
11 | public enum UnixPermissions
12 | {
13 | // Owner
14 | OwnerRead = 400,
15 | OwnerWrite = 200,
16 | OwnerExecute = 100,
17 |
18 | // Group
19 | GroupRead = 40,
20 | GroupWrite = 20,
21 | GroupExecute = 10,
22 |
23 | // Others
24 | OthersRead = 4,
25 | OthersWrite = 2,
26 | OthersExecute = 1,
27 |
28 | // Common permissions
29 | OwnerReadWrite = 600,
30 | OwnerReadWriteExecute = 700,
31 | GroupAndOthersReadExecute = 55,
32 | AllReadWriteExecute = 777,
33 | AllReadWrite = 666,
34 | Default = 644
35 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Extensions/FileStreamExtensions.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 30.05.2024 - 11:05:42
5 | // Last edit: 30.05.2024 - 11:05:42
6 |
7 | #endregion
8 |
9 | using System.Diagnostics;
10 | using OpenSSH_GUI.Core.Enums;
11 |
12 | namespace OpenSSH_GUI.Core.Extensions;
13 |
14 | public static class FileStreamExtensions
15 | {
16 | public static async Task WithUnixPermissionsAsync(this FileStream stream, UnixPermissions permission)
17 | {
18 | if (Environment.OSVersion.Platform is PlatformID.Unix or PlatformID.MacOSX)
19 | {
20 | using var proc = new Process();
21 | proc.StartInfo.FileName = "/bin/bash";
22 | proc.StartInfo.ArgumentList.Add("-c");
23 | proc.StartInfo.ArgumentList.Add($"chmod {(int)permission} {stream.Name}");
24 | proc.Start();
25 | await proc.WaitForExitAsync();
26 | if (proc.ExitCode != 0)
27 | throw new InvalidOperationException($"Chmod execution failed with exit code : {proc.ExitCode}");
28 | }
29 |
30 | return stream;
31 | }
32 |
33 | public static FileStream WithUnixPermissions(this FileStream stream, UnixPermissions permission)
34 | {
35 | return stream.WithUnixPermissionsAsync(permission).GetAwaiter().GetResult();
36 | }
37 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Extensions/IConnectionCredentialsExtension.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:24
6 |
7 | #endregion
8 |
9 | using Microsoft.EntityFrameworkCore;
10 | using OpenSSH_GUI.Core.Database.Context;
11 | using OpenSSH_GUI.Core.Database.DTO;
12 | using OpenSSH_GUI.Core.Interfaces.Credentials;
13 |
14 | namespace OpenSSH_GUI.Core.Extensions;
15 |
16 | ///
17 | /// Extension class for connection credentials.
18 | ///
19 | public static class ConnectionCredentialsExtensions
20 | {
21 | ///
22 | /// Saves the given connection credentials DTO in the database.
23 | ///
24 | /// The connection credentials.
25 | /// A task representing the asynchronous operation. The task result is the saved connection credentials DTO.
26 | public static async Task SaveDtoInDatabase(this IConnectionCredentials cc)
27 | {
28 | await using var dbContext = new OpenSshGuiDbContext();
29 |
30 | var foundDuplicate = (cc is IMultiKeyConnectionCredentials or IPasswordConnectionCredentials
31 | ? await dbContext.ConnectionCredentialsDtos.FirstOrDefaultAsync(c =>
32 | c.Hostname == cc.Hostname &&
33 | c.Username == cc.Username &&
34 | c.AuthType == cc.AuthType)
35 | : null) ?? (cc is IKeyConnectionCredentials dkcc
36 | ? await dbContext.ConnectionCredentialsDtos.FirstOrDefaultAsync(c => c.Hostname == cc.Hostname &&
37 | c.Username == cc.Username &&
38 | c.AuthType == cc.AuthType &&
39 | c.KeyDtos.FirstOrDefault(e => e.AbsolutePath != dkcc.Key.AbsoluteFilePath) == null)
40 | : null);
41 | if (foundDuplicate is not null) return null;
42 | ConnectionCredentialsDto baseDto = new()
43 | {
44 | Id = cc.Id,
45 | Hostname = cc.Hostname,
46 | Username = cc.Username,
47 | AuthType = cc.AuthType,
48 | KeyDtos = []
49 | };
50 | var entity = dbContext.ConnectionCredentialsDtos.Add(baseDto);
51 | await dbContext.SaveChangesAsync();
52 | baseDto = entity.Entity;
53 |
54 | switch (cc)
55 | {
56 | case IPasswordConnectionCredentials pcc:
57 | baseDto.Password = pcc.Password;
58 | baseDto.PasswordEncrypted = pcc.EncryptedPassword;
59 | break;
60 | case IKeyConnectionCredentials kcc:
61 | baseDto.KeyDtos.Add(dbContext.KeyDtos.First(e => e.AbsolutePath == kcc.Key.AbsoluteFilePath));
62 | break;
63 | case IMultiKeyConnectionCredentials mcc:
64 | var dbKeys = dbContext.KeyDtos.Where(a =>
65 | mcc.Keys.Select(b => b.AbsoluteFilePath).Contains(a.AbsolutePath));
66 | baseDto.KeyDtos.AddRange(dbKeys);
67 | break;
68 | }
69 |
70 | await dbContext.SaveChangesAsync();
71 | return await dbContext.ConnectionCredentialsDtos.FindAsync(baseDto.Id);
72 | }
73 |
74 | public static async Task RemoveDtoFromDatabase(this IConnectionCredentials cc)
75 | {
76 | await using var dbContext = new OpenSshGuiDbContext();
77 | await dbContext.ConnectionCredentialsDtos.Where(e => e.Id == cc.Id).ExecuteDeleteAsync();
78 | await dbContext.SaveChangesAsync();
79 | }
80 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Extensions/IPrivateKeySourceExtensions.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 16.05.2024 - 08:05:28
3 | // Last edit: 16.05.2024 - 08:05:28
4 |
5 | using Renci.SshNet;
6 | using SshNet.Keygen.Extensions;
7 |
8 | namespace OpenSSH_GUI.Core.Extensions;
9 |
10 | ///
11 | /// Provides extension methods for the IPrivateKeySource interface.
12 | ///
13 | public static class PrivateKeySourceExtensions
14 | {
15 | ///
16 | /// Retrieves the fingerprint hash of the private key source.
17 | ///
18 | /// The private key source.
19 | /// The fingerprint hash of the private key source.
20 | public static string FingerprintHash(this IPrivateKeySource privateKeySource)
21 | {
22 | return privateKeySource
23 | .Fingerprint()
24 | .Split(' ')
25 | .First(e => e.StartsWith("SHA"))
26 | .Split(':')
27 | .Last();
28 | }
29 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Extensions/KeyTypeExtension.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:24
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 | using OpenSSH_GUI.Core.Interfaces.Keys;
11 | using OpenSSH_GUI.Core.Lib.Keys;
12 |
13 | namespace OpenSSH_GUI.Core.Extensions;
14 |
15 | ///
16 | /// Provides extension methods for working with key types.
17 | ///
18 | public static class KeyTypeExtension
19 | {
20 | ///
21 | /// Retrieves available SSH key types.
22 | ///
23 | /// The collection of available SSH key types.
24 | public static IEnumerable GetAvailableKeyTypes()
25 | {
26 | return Enum.GetValues().Select(keyType => new SshKeyType(keyType)).ToList();
27 | }
28 |
29 |
30 | ///
31 | /// Retrieves the possible bit values for a given key type.
32 | ///
33 | /// The key type.
34 | /// An enumerable of possible bit values.
35 | public static IEnumerable GetBitValues(this KeyType type)
36 | {
37 | return type switch
38 | {
39 | KeyType.RSA => [1024, 2048, 3072, 4096],
40 | KeyType.ECDSA => [256, 384, 521],
41 | _ => []
42 | };
43 | }
44 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Extensions/SshConfigFilesExtension.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:25
6 |
7 | #endregion
8 |
9 | using Microsoft.Extensions.Logging;
10 | using Microsoft.Extensions.Logging.Abstractions;
11 | using OpenSSH_GUI.Core.Enums;
12 |
13 | namespace OpenSSH_GUI.Core.Extensions;
14 |
15 | ///
16 | /// Provides extension methods for handling SSH configuration files.
17 | ///
18 | public static class SshConfigFilesExtension
19 | {
20 | ///
21 | /// Represents the path for SSH with variable on Linux.
22 | ///
23 | private const string SshPathWithVariableLinux = "%HOME%/.ssh";
24 |
25 | ///
26 | /// Represents the SSH path with a variable for Windows operating system.
27 | ///
28 | private const string SshPathWithVariableWindows = "%USERPROFILE%\\.ssh";
29 |
30 | ///
31 | /// Represents the root SSH path for Linux.
32 | ///
33 | private const string SshRootPathLinux = "/etc/ssh";
34 |
35 | ///
36 | /// The SSH root path on Windows.
37 | ///
38 | private const string SshRootPathWin = "%PROGRAMDATA%\\ssh";
39 |
40 | public static void ValidateDirectories(ILogger? logger = null)
41 | {
42 | logger ??= NullLogger.Instance;
43 | try
44 | {
45 | if (!Directory.Exists(GetRootSshPath())) Directory.CreateDirectory(GetRootSshPath());
46 | if (!Directory.Exists(GetBaseSshPath())) Directory.CreateDirectory(GetBaseSshPath());
47 | }
48 | catch (Exception e)
49 | {
50 | logger.LogError(e, "Error creating config path");
51 | }
52 | }
53 |
54 | ///
55 | /// Retrieves the root SSH path based on the platform.
56 | ///
57 | /// Indicates whether to resolve environment variables in the path. Defaults to true.
58 | /// The platform ID. Defaults to the current operating system platform.
59 | /// The root SSH path.
60 | public static string GetRootSshPath(bool resolve = true, PlatformID? platformId = null)
61 | {
62 | var path = (platformId ?? Environment.OSVersion.Platform) switch
63 | {
64 | PlatformID.Win32S or PlatformID.Win32Windows or PlatformID.Win32NT or PlatformID.WinCE => SshRootPathWin,
65 | PlatformID.Unix or PlatformID.MacOSX => SshRootPathLinux,
66 | _ => throw new NotSupportedException(
67 | $"Platform {Environment.OSVersion.Platform.ToString().ToLower()} is not supported!")
68 | };
69 | return resolve ? Environment.ExpandEnvironmentVariables(path) : path;
70 | }
71 |
72 | /// Gets the base path to the SSH directory.
73 | /// @param resolve Boolean value indicating whether to resolve environment variables in the path. Default is true.
74 | /// @param platformId (optional) The platform ID. Default is the platform of the current environment.
75 | /// @returns The base path to the SSH directory.
76 | /// /
77 | public static string GetBaseSshPath(bool resolve = true, PlatformID? platformId = null)
78 | {
79 | var path = (platformId ?? Environment.OSVersion.Platform) switch
80 | {
81 | PlatformID.Win32S or PlatformID.Win32Windows or PlatformID.Win32NT or PlatformID.WinCE =>
82 | SshPathWithVariableWindows,
83 | PlatformID.Unix or PlatformID.MacOSX => SshPathWithVariableLinux,
84 | _ => throw new NotSupportedException(
85 | $"Platform {Environment.OSVersion.Platform.ToString().ToLower()} is not supported!")
86 | };
87 | return resolve ? Environment.ExpandEnvironmentVariables(path) : path;
88 | }
89 |
90 | ///
91 | /// Retrieves the file path corresponding to the specified enum value.
92 | ///
93 | /// The enum value representing the SSH config file.
94 | ///
95 | /// A boolean value indicating whether to resolve the file path using
96 | /// or .
97 | ///
98 | /// The target platform identifier to determine the file path format.
99 | /// The file path as a .
100 | public static string GetPathOfFile(this SshConfigFiles files, bool resolve = true, PlatformID? platform = null)
101 | {
102 | var path = Path.Combine(files switch
103 | {
104 | SshConfigFiles.Authorized_Keys or
105 | SshConfigFiles.Known_Hosts or
106 | SshConfigFiles.Config => GetBaseSshPath(resolve, platform),
107 | SshConfigFiles.Sshd_Config => GetRootSshPath(resolve, platform),
108 | _ => throw new ArgumentException("Invalid value for \"files\"")
109 | }, Enum.GetName(files)!.ToLower());
110 | platform ??= Environment.OSVersion.Platform;
111 | path = platform is PlatformID.Unix ? path.Replace('\\', '/') : path.Replace('/', '\\');
112 | return path;
113 | }
114 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Extensions/SshKeyFormatExtension.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 18.05.2024 - 14:05:52
3 | // Last edit: 18.05.2024 - 14:05:52
4 |
5 | using SshNet.Keygen;
6 |
7 | namespace OpenSSH_GUI.Core.Extensions;
8 |
9 | ///
10 | /// Provides extension methods for working with SSH key formats.
11 | ///
12 | public static class SshKeyFormatExtension
13 | {
14 | ///
15 | /// Represents the file extension for OpenSSH Public Key format.
16 | ///
17 | private const string OpenSshPublicKeyExtension = ".pub";
18 |
19 | ///
20 | /// Represents the file extension used for PuTTY private key files.
21 | ///
22 | private const string PuttyKeyExtension = ".ppk";
23 |
24 | ///
25 | /// Returns the file extension associated with the specified SSH key format.
26 | ///
27 | /// The SSH key format.
28 | /// True if the extension is for a public key; otherwise, false. Defaults to true.
29 | ///
30 | /// The file extension associated with the specified SSH key format. Returns null if the format is not supported
31 | /// or the extension is not applicable.
32 | ///
33 | public static string? GetExtension(this SshKeyFormat format, bool @public = true)
34 | {
35 | return format switch
36 | {
37 | SshKeyFormat.OpenSSH when @public => OpenSshPublicKeyExtension,
38 | SshKeyFormat.OpenSSH => null,
39 | SshKeyFormat.PuTTYv2 or SshKeyFormat.PuTTYv3 => PuttyKeyExtension,
40 | _ => null
41 | };
42 | }
43 |
44 | ///
45 | /// Changes the file extension of the given path to match the specified SSH key format.
46 | ///
47 | /// The SSH key format.
48 | /// The path to the file.
49 | /// Indicates whether the key is public. Default is true.
50 | /// The modified file path with the updated extension.
51 | public static string ChangeExtension(this SshKeyFormat format, string path, bool @public = true)
52 | {
53 | return Path.ChangeExtension(path, format.GetExtension(@public));
54 | }
55 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Extensions/SshKeyGenerateParamsExtensions.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 18.05.2024 - 13:05:14
3 | // Last edit: 18.05.2024 - 13:05:14
4 |
5 | using OpenSSH_GUI.Core.Enums;
6 | using OpenSSH_GUI.Core.Lib.Misc;
7 | using SshNet.Keygen;
8 | using SshNet.Keygen.SshKeyEncryption;
9 |
10 | namespace OpenSSH_GUI.Core.Extensions;
11 |
12 | ///
13 | /// Extensions for the class.
14 | ///
15 | public static class SshKeyGenerateParamsExtensions
16 | {
17 | ///
18 | /// Converts the given object to an object.
19 | ///
20 | /// The object to convert.
21 | /// An object.
22 | public static SshKeyGenerateInfo ToInfo(this SshKeyGenerateParams @params)
23 | {
24 | return new SshKeyGenerateInfo
25 | {
26 | KeyType = @params.KeyType switch
27 | {
28 | KeyType.RSA => SshKeyType.RSA,
29 | KeyType.ECDSA => SshKeyType.ECDSA,
30 | KeyType.ED25519 => SshKeyType.ED25519,
31 | _ => throw new ArgumentOutOfRangeException()
32 | },
33 | Comment = @params.Comment,
34 | KeyFormat = @params.KeyFormat,
35 | KeyLength = @params.KeyLength,
36 | Encryption = !string.IsNullOrWhiteSpace(@params.Password)
37 | ? new SshKeyEncryptionAes256(@params.Password,
38 | @params.KeyFormat is not SshKeyFormat.OpenSSH ? new PuttyV3Encryption() : null)
39 | : new SshKeyEncryptionNone()
40 | };
41 | }
42 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/AuthorizedKeys/IAuthorizedKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:35
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 |
11 | namespace OpenSSH_GUI.Core.Interfaces.AuthorizedKeys;
12 |
13 | ///
14 | /// Represents an authorized key.
15 | ///
16 | public interface IAuthorizedKey
17 | {
18 | ///
19 | /// Enumeration for SSH key types.
20 | ///
21 | KeyType KeyType { get; }
22 |
23 | ///
24 | /// Represents an authorized key entry in an authorized keys file.
25 | ///
26 | string Fingerprint { get; }
27 |
28 | ///
29 | /// Represents an authorized key entry in an authorized keys file.
30 | ///
31 | string Comment { get; }
32 |
33 | /// *Property: MarkedForDeletion**
34 | bool MarkedForDeletion { get; set; }
35 |
36 | ///
37 | /// Returns the full key entry of an authorized key.
38 | ///
39 | /// The full key entry string.
40 | string GetFullKeyEntry { get; }
41 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/AuthorizedKeys/IAuthorizedKeysFile.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:35
6 |
7 | #endregion
8 |
9 | using System.Collections.ObjectModel;
10 | using OpenSSH_GUI.Core.Interfaces.Keys;
11 | using ReactiveUI;
12 |
13 | namespace OpenSSH_GUI.Core.Interfaces.AuthorizedKeys;
14 |
15 | ///
16 | /// Interface for an Authorized Keys file.
17 | ///
18 | public interface IAuthorizedKeysFile : IReactiveObject
19 | {
20 | ///
21 | /// Represents the authorized keys file.
22 | ///
23 | ObservableCollection AuthorizedKeys { get; set; }
24 |
25 | ///
26 | /// Adds an authorized key to the authorized keys file.
27 | ///
28 | /// The SSH key to be added.
29 | /// True if the key was successfully added, otherwise false.
30 | bool AddAuthorizedKey(ISshKey key);
31 |
32 | ///
33 | /// Applies the changes to the authorized keys file.
34 | ///
35 | /// The collection of keys to be applied as changes.
36 | /// True if any changes were made to the authorized keys file; otherwise, false.
37 | bool ApplyChanges(IEnumerable keys);
38 |
39 | ///
40 | /// Persists the changes made to the authorized keys file.
41 | ///
42 | /// The modified object.
43 | IAuthorizedKeysFile PersistChangesInFile();
44 |
45 | ///
46 | /// Adds an authorized key asynchronously.
47 | ///
48 | /// The SSH key to be added.
49 | ///
50 | /// A task representing the asynchronous operation. The task result is a boolean value indicating whether the key
51 | /// was added successfully.
52 | ///
53 | Task AddAuthorizedKeyAsync(ISshKey key);
54 |
55 | ///
56 | /// Removes the specified SSH key from the authorized keys list.
57 | ///
58 | /// The SSH key to remove.
59 | ///
60 | /// Returns true if the key is successfully removed;
61 | /// otherwise, false.
62 | ///
63 | bool RemoveAuthorizedKey(ISshKey key);
64 |
65 | ///
66 | /// Exports the content of the authorized keys file.
67 | ///
68 | /// Indicates whether to export for the local machine or remote server. Default is true (local).
69 | ///
70 | /// The platform ID of the server. If null, the current OS platform will be used. Only applicable if
71 | /// 'local' is set to false.
72 | ///
73 | /// The content of the authorized keys file as a string.
74 | string ExportFileContent(bool local = true, PlatformID? platform = null);
75 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Credentials/IConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:34
6 |
7 | #endregion
8 |
9 | using System.Text.Json.Serialization;
10 | using OpenSSH_GUI.Core.Enums;
11 | using Renci.SshNet;
12 |
13 | namespace OpenSSH_GUI.Core.Interfaces.Credentials;
14 |
15 | ///
16 | /// Represents the interface for connection credentials.
17 | ///
18 | public interface IConnectionCredentials
19 | {
20 | ///
21 | /// Represents the unique identifier for a connection credential.
22 | ///
23 | ///
24 | /// The ID is used to uniquely identify a connection credential in the system.
25 | ///
26 | int Id { get; set; }
27 |
28 | ///
29 | /// Represents the host name for a connection.
30 | ///
31 | ///
32 | /// The host name is an essential property for establishing a connection to a remote server.
33 | /// It identifies the target server that the client wants to connect to.
34 | ///
35 | string Hostname { get; set; }
36 |
37 | ///
38 | /// Represents the base class for connection credentials.
39 | ///
40 | int Port { get; }
41 |
42 | ///
43 | /// Represents the username used for the connection credentials.
44 | ///
45 | string Username { get; set; }
46 |
47 | ///
48 | /// Gets the display string representation of the connection credentials.
49 | ///
50 | /// The display string in the format "{Username}@{Hostname}:{Port}"
51 | [JsonIgnore]
52 | string Display { get; }
53 |
54 | ///
55 | /// Gets the authentication type for the connection credentials.
56 | ///
57 | ///
58 | /// The property represents the type of authentication to be used for the connection
59 | /// credentials.
60 | /// The available authentication types are:
61 | /// - Password: The connection credentials use password authentication.
62 | /// - Key: The connection credentials use key authentication.
63 | /// - MultiKey: The connection credentials use multi-key authentication.
64 | ///
65 | AuthType AuthType { get; }
66 |
67 | ///
68 | /// Retrieves the connection information based on the provided credentials.
69 | ///
70 | /// The object representing the SSH connection information.
71 | ConnectionInfo GetConnectionInfo();
72 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Credentials/IKeyConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:34
6 |
7 | #endregion
8 |
9 | using System.Text.Json.Serialization;
10 | using OpenSSH_GUI.Core.Interfaces.Keys;
11 |
12 | namespace OpenSSH_GUI.Core.Interfaces.Credentials;
13 |
14 | ///
15 | /// Represents connection credentials for SSH using key-based authentication.
16 | ///
17 | public interface IKeyConnectionCredentials : IConnectionCredentials
18 | {
19 | ///
20 | /// Represents a connection credential that includes an SSH key.
21 | ///
22 | [JsonIgnore]
23 | ISshKey? Key { get; set; }
24 |
25 | ///
26 | /// Renews the SSH key used for authentication.
27 | ///
28 | /// The password for the key file (optional).
29 | void RenewKey(string? password = null);
30 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Credentials/IMultiKeyConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:35
6 |
7 | #endregion
8 |
9 | using System.Text.Json.Serialization;
10 | using OpenSSH_GUI.Core.Interfaces.Keys;
11 |
12 | namespace OpenSSH_GUI.Core.Interfaces.Credentials;
13 |
14 | ///
15 | /// Represents the interface for multi-key connection credentials.
16 | ///
17 | public interface IMultiKeyConnectionCredentials : IConnectionCredentials
18 | {
19 | ///
20 | /// Represents the credentials for a multi-key connection.
21 | ///
22 | [JsonIgnore]
23 | IEnumerable? Keys { get; set; }
24 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Credentials/IPasswordConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:35
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Interfaces.Credentials;
10 |
11 | ///
12 | /// Represents the interface for password-based connection credentials.
13 | ///
14 | public interface IPasswordConnectionCredentials : IConnectionCredentials
15 | {
16 | ///
17 | /// Represents the password connection credentials.
18 | ///
19 | string Password { get; set; }
20 |
21 | ///
22 | /// Gets or sets a value indicating whether the password is encrypted.
23 | ///
24 | bool EncryptedPassword { get; set; }
25 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Keys/IPpkKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:32
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 |
11 | namespace OpenSSH_GUI.Core.Interfaces.Keys;
12 |
13 | ///
14 | /// Represents an interface for a PpkKey.
15 | ///
16 | public interface IPpkKey : ISshKey
17 | {
18 | ///
19 | /// Represents the encryption type of an SSH key.
20 | ///
21 | EncryptionType EncryptionType { get; }
22 |
23 | ///
24 | /// Gets the public key string of the SSH key.
25 | ///
26 | ///
27 | /// This property represents the public key string of the SSH key.
28 | ///
29 | string PublicKeyString { get; }
30 |
31 | ///
32 | /// Represents the private key string of an SSH key.
33 | ///
34 | ///
35 | /// The private key string.
36 | ///
37 | string PrivateKeyString { get; }
38 |
39 | ///
40 | /// Represents a PPK key.
41 | ///
42 | string PrivateMAC { get; }
43 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Keys/ISshKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:33
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Interfaces.Misc;
10 |
11 | namespace OpenSSH_GUI.Core.Interfaces.Keys;
12 |
13 | ///
14 | /// Represents an SSH key.
15 | ///
16 | public interface ISshKey : IKeyBase
17 | {
18 | ///
19 | /// Represents the unique identifier for an SSH key.
20 | ///
21 | int Id { get; set; }
22 |
23 | ///
24 | /// Represents a comment associated with an SSH key.
25 | ///
26 | string Comment { get; }
27 |
28 | ///
29 | /// Gets a value indicating whether the SSH key is a Putty key.
30 | ///
31 | ///
32 | /// The SSH key is considered a Putty key if its format is not OpenSSH.
33 | ///
34 | bool IsPuttyKey { get; }
35 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Keys/ISshKeyType.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:33
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 |
11 | namespace OpenSSH_GUI.Core.Interfaces.Keys;
12 |
13 | ///
14 | /// Represents an SSH key type.
15 | ///
16 | public interface ISshKeyType
17 | {
18 | ///
19 | /// Interface for SSH key types.
20 | ///
21 | KeyType BaseType { get; }
22 |
23 | ///
24 | /// Represents an SSH key type.
25 | ///
26 | string KeyTypeText { get; }
27 |
28 | ///
29 | /// Specifies whether the SSH key type has a default bit size.
30 | ///
31 | bool HasDefaultBitSize { get; }
32 |
33 | ///
34 | /// Gets or sets the current bit size of the SSH key type.
35 | ///
36 | int CurrentBitSize { get; set; }
37 |
38 | ///
39 | /// Represents a property that provides possible bit sizes for SSH key types.
40 | ///
41 | IEnumerable PossibleBitSizes { get; }
42 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Keys/ISshPrivateKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:33
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Interfaces.Keys;
10 |
11 | ///
12 | /// Represents an SSH private key.
13 | ///
14 | public interface ISshPrivateKey : ISshKey;
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Keys/ISshPublicKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:33
6 |
7 | #endregion
8 |
9 | namespace OpenSSH_GUI.Core.Interfaces.Keys;
10 |
11 | ///
12 | /// Represents a SSH public key.
13 | ///
14 | public interface ISshPublicKey : ISshKey
15 | {
16 | ///
17 | /// Represents the private key associated with an SSH public key.
18 | ///
19 | ISshKey PrivateKey { get; }
20 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/KnownHosts/IKnownHost.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:34
6 |
7 | #endregion
8 |
9 | using ReactiveUI;
10 |
11 | namespace OpenSSH_GUI.Core.Interfaces.KnownHosts;
12 |
13 | ///
14 | /// Represents a known host in the OpenSSH GUI.
15 | ///
16 | public interface IKnownHost : IReactiveObject
17 | {
18 | ///
19 | /// Represents a known host.
20 | ///
21 | string Host { get; }
22 |
23 | ///
24 | /// Represents a known host in the OpenSSH GUI.
25 | ///
26 | bool DeleteWholeHost { get; }
27 |
28 | ///
29 | /// Represents a known host in the OpenSSH GUI.
30 | ///
31 | List Keys { get; set; }
32 |
33 | ///
34 | /// Toggles the marked for deletion flag of each within the list.
35 | /// If the property is true, it sets the flag to false for all keys. Otherwise, it sets
36 | /// the flag to true for all keys.
37 | ///
38 | void KeysDeletionSwitch();
39 |
40 | ///
41 | /// Retrieves all entries for a known host in the known hosts file.
42 | ///
43 | ///
44 | /// Returns a string containing all the entries for the known host.
45 | /// If the entire host is marked for deletion, returns the line ending character.
46 | ///
47 | string GetAllEntries();
48 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/KnownHosts/IKnownHostKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:34
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 | using ReactiveUI;
11 |
12 | namespace OpenSSH_GUI.Core.Interfaces.KnownHosts;
13 |
14 | /// Represents a known host key in the OpenSSH GUI.
15 | /// /
16 | public interface IKnownHostKey : IReactiveObject
17 | {
18 | ///
19 | /// Represents the type of a known host key.
20 | ///
21 | KeyType KeyType { get; }
22 |
23 | ///
24 | /// Represents a known host key in the OpenSSH GUI.
25 | ///
26 | string Fingerprint { get; }
27 |
28 | ///
29 | /// Represents a known host key in the OpenSSH GUI.
30 | ///
31 | string EntryWithoutHost { get; }
32 |
33 | ///
34 | /// Gets or sets whether the KnownHostKey is marked for deletion.
35 | ///
36 | bool MarkedForDeletion { get; set; }
37 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/KnownHosts/IKnownHostsFile.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:34
6 |
7 | #endregion
8 |
9 | using System.Collections.ObjectModel;
10 | using ReactiveUI;
11 |
12 | namespace OpenSSH_GUI.Core.Interfaces.KnownHosts;
13 |
14 | ///
15 | /// Represents a known hosts file.
16 | ///
17 | public interface IKnownHostsFile : IReactiveObject
18 | {
19 | ///
20 | /// Represents the line ending character used in the known_hosts file.
21 | ///
22 | static string LineEnding { get; set; }
23 |
24 | ///
25 | /// Represents a file that contains known host entries.
26 | ///
27 | ObservableCollection KnownHosts { get; }
28 |
29 | ///
30 | /// Asynchronously reads the contents of the known hosts file.
31 | ///
32 | ///
33 | /// The file stream to read from. If null, the method reads from the file specified in the
34 | /// constructor.
35 | ///
36 | /// A task representing the asynchronous operation.
37 | Task ReadContentAsync(FileStream? stream = null);
38 |
39 | ///
40 | /// Synchronizes the known hosts with the given list of new known hosts.
41 | ///
42 | /// The new known hosts to synchronize.
43 | void SyncKnownHosts(IEnumerable newKnownHosts);
44 |
45 | ///
46 | /// Updates the content of the known hosts file.
47 | ///
48 | /// A task representing the update operation.
49 | Task UpdateFile();
50 |
51 | ///
52 | /// Retrieves the updated contents of the known hosts file.
53 | ///
54 | /// The platform ID of the server.
55 | /// The updated contents of the known hosts file as a string.
56 | ///
57 | /// This method retrieves the updated contents of the known hosts file.
58 | /// It takes the platform ID of the server as a parameter and returns the
59 | /// updated contents of the known hosts file as a string. The method
60 | /// checks if the instance of the KnownHostsFile class is created from
61 | /// the server or not. If it is not created from the server, it returns
62 | /// an empty string. It sets the LineEnding property based on the platform
63 | /// ID provided. It then aggregates the known hosts entries excluding those
64 | /// which are flagged for deletion and returns the updated contents as a string.
65 | ///
66 | /// The platform ID of the server.
67 | string GetUpdatedContents(PlatformID platformId);
68 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Misc/IKeyBase.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 01:05:51
5 | // Last edit: 15.05.2024 - 01:05:48
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Database.DTO;
10 | using OpenSSH_GUI.Core.Interfaces.AuthorizedKeys;
11 | using OpenSSH_GUI.Core.Interfaces.Keys;
12 | using Renci.SshNet;
13 | using SshNet.Keygen;
14 |
15 | namespace OpenSSH_GUI.Core.Interfaces.Misc;
16 |
17 | ///
18 | /// Represents the base interface for SSH keys.
19 | ///
20 | public interface IKeyBase
21 | {
22 | ///
23 | /// Gets a value indicating whether the SSH key has a password.
24 | ///
25 | bool HasPassword { get; }
26 |
27 | ///
28 | /// Gets a value indicating whether the key requires a password.
29 | ///
30 | ///
31 | /// This property is calculated based on the and properties.
32 | /// If the key has a password and the password has not been successfully provided, then this property will be true.
33 | ///
34 | bool NeedPassword { get; }
35 |
36 | ///
37 | /// Gets or sets a value indicating whether the password authentication was successful for the SSH key.
38 | ///
39 | bool PasswordSuccess { get; set; }
40 |
41 | ///
42 | /// Represents a password associated with a key.
43 | ///
44 | string? Password { get; set; }
45 |
46 | ///
47 | /// Gets the absolute file path of the SSH key.
48 | ///
49 | /// The absolute file path of the SSH key.
50 | string AbsoluteFilePath { get; }
51 |
52 | ///
53 | /// Represents a fingerprint of a key.
54 | ///
55 | string Fingerprint { get; }
56 |
57 | ///
58 | /// Gets the filename of the key.
59 | ///
60 | string Filename { get; }
61 |
62 | /// Gets the format of the SSH key.
63 | /// @return The format of the SSH key.
64 | /// /
65 | SshKeyFormat Format { get; }
66 |
67 | ///
68 | /// Represents the key type for an SSH key.
69 | ///
70 | ISshKeyType KeyType { get; }
71 |
72 | ///
73 | /// Export the public key in OpenSSH format.
74 | ///
75 | /// The public key in OpenSSH format.
76 | string? ExportOpenSshPublicKey();
77 |
78 | ///
79 | /// Exports the private key in OpenSSH format.
80 | ///
81 | /// The private key in OpenSSH format as a string, or null if the key source is null.
82 | string? ExportOpenSshPrivateKey();
83 |
84 | ///
85 | /// Exports the public key in PuTTY format.
86 | ///
87 | /// The public key in PuTTY format.
88 | string? ExportPuttyPublicKey();
89 |
90 | ///
91 | /// Exports the Putty PPK key format of the SSH key.
92 | ///
93 | ///
94 | /// The Putty PPK key format as a string.
95 | ///
96 | string? ExportPuttyPpkKey();
97 |
98 | ///
99 | /// Exports the text representation of the key.
100 | ///
101 | /// The text representation of the key.
102 | string? ExportTextOfKey();
103 |
104 | ///
105 | /// Exports the authorized key entry for the key.
106 | ///
107 | /// The authorized key entry for the key.
108 | string? ExportAuthorizedKeyEntry();
109 |
110 | ///
111 | /// Exports the authorized key as an instance of IAuthorizedKey.
112 | ///
113 | /// The exported authorized key.
114 | IAuthorizedKey ExportAuthorizedKey();
115 |
116 | ///
117 | /// Retrieves the SSH.NET key type of the key.
118 | ///
119 | /// The SSH.NET key type of the key.
120 | IPrivateKeySource? GetSshNetKeyType();
121 |
122 | ///
123 | /// Deletes the SSH key.
124 | ///
125 | void DeleteKey();
126 |
127 | ///
128 | /// Converts the current instance of the class to an instance of .
129 | ///
130 | /// An instance of .
131 | SshKeyDto ToDto();
132 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Interfaces/Misc/IServerConnection.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:33
6 |
7 | #endregion
8 |
9 | using System.Diagnostics.CodeAnalysis;
10 | using OpenSSH_GUI.Core.Interfaces.AuthorizedKeys;
11 | using OpenSSH_GUI.Core.Interfaces.Credentials;
12 | using OpenSSH_GUI.Core.Interfaces.KnownHosts;
13 | using ReactiveUI;
14 |
15 | namespace OpenSSH_GUI.Core.Interfaces.Misc;
16 |
17 | /// Represents a server connection.
18 | /// /
19 | public interface IServerConnection : IReactiveObject, IDisposable
20 | {
21 | ///
22 | /// Represents the credentials for a server connection.
23 | ///
24 | IConnectionCredentials ConnectionCredentials { get; }
25 |
26 | ///
27 | /// Gets or sets the connection time of the server.
28 | ///
29 | DateTime ConnectionTime { get; set; }
30 |
31 | ///
32 | /// Gets or sets a value indicating whether the server connection is currently open.
33 | ///
34 | bool IsConnected { get; set; }
35 |
36 | ///
37 | /// Represents a server connection.
38 | ///
39 | string ConnectionString { get; }
40 |
41 | ///
42 | /// Represents a server connection.
43 | ///
44 | PlatformID ServerOs { get; set; }
45 |
46 | ///
47 | /// Tests the connection to the server and opens a connection if successful.
48 | ///
49 | ///
50 | /// An that represents the error that occurred during the test and open
51 | /// connection process, if any. If the test and open connection process is successful, this value is null.
52 | ///
53 | /// true if the test and open connection process is successful; otherwise, false.
54 | bool TestAndOpenConnection([NotNullWhen(false)] out Exception? exception);
55 |
56 | ///
57 | /// Closes the server connection.
58 | ///
59 | /// An exception that occurred during the closing of the connection, if any.
60 | /// Returns true if the connection was closed successfully, otherwise false.
61 | bool CloseConnection([NotNullWhen(false)] out Exception? ex);
62 |
63 | ///
64 | /// Retrieves the known hosts file from the server.
65 | ///
66 | /// The known hosts file as an instance of IKnownHostsFile.
67 | IKnownHostsFile GetKnownHostsFromServer();
68 |
69 | ///
70 | /// Writes the known hosts file to the server.
71 | ///
72 | /// The known hosts file to write.
73 | /// Returns true if the known hosts file was successfully written to the server, false otherwise.
74 | bool WriteKnownHostsToServer(IKnownHostsFile knownHostsFile);
75 |
76 | ///
77 | /// Retrieves the authorized keys file from the server.
78 | ///
79 | /// The authorized keys file from the server.
80 | IAuthorizedKeysFile GetAuthorizedKeysFromServer();
81 |
82 | ///
83 | /// Writes the changes made to the authorized keys file to the server.
84 | ///
85 | /// The authorized keys file containing the changes.
86 | /// True if the changes were successfully written to the server; otherwise, false.
87 | bool WriteAuthorizedKeysChangesToServer(IAuthorizedKeysFile authorizedKeysFile);
88 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/AuthorizedKeys/AuthorizedKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:32
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 | using OpenSSH_GUI.Core.Interfaces.AuthorizedKeys;
11 |
12 | namespace OpenSSH_GUI.Core.Lib.AuthorizedKeys;
13 |
14 | ///
15 | /// Represents an authorized key entry in an authorized keys file.
16 | ///
17 | public class AuthorizedKey : IAuthorizedKey
18 | {
19 | ///
20 | /// Represents an authorized key entry in the authorized_keys file.
21 | ///
22 | public AuthorizedKey(string keyEntry)
23 | {
24 | var split = keyEntry.Split(' ');
25 | if (split.Length != 3)
26 | throw new IndexOutOfRangeException("Authorized Keys must contain TYPE FINGERPRINT COMMENT");
27 |
28 | KeyTypeDeclarationInFile = split[0];
29 | KeyType = Enum.Parse(
30 | KeyTypeDeclarationInFile.StartsWith("ssh-")
31 | ? KeyTypeDeclarationInFile.Replace("ssh-", "")
32 | : KeyTypeDeclarationInFile.Split('-')[0], true);
33 | Fingerprint = split[1];
34 | Comment = split[2];
35 | }
36 |
37 | ///
38 | /// Represents the key type declaration in the authorized key file.
39 | ///
40 | ///
41 | /// This property holds the key type declaration as it appears in the authorized key file.
42 | /// The key type declaration indicates the algorithm used for the key.
43 | ///
44 | private string KeyTypeDeclarationInFile { get; }
45 |
46 | ///
47 | /// Represents the type of an authorized key.
48 | ///
49 | public KeyType KeyType { get; }
50 |
51 | ///
52 | /// Represents an authorized key entry.
53 | ///
54 | public string Fingerprint { get; }
55 |
56 | ///
57 | /// Represents an authorized key entry.
58 | ///
59 | public string Comment { get; }
60 |
61 | ///
62 | /// Represents an authorized key.
63 | ///
64 | public bool MarkedForDeletion { get; set; }
65 |
66 | ///
67 | /// Gets the full key entry string of an authorized key.
68 | ///
69 | ///
70 | /// The full key entry string consists of the key type, fingerprint, and comment separated by spaces.
71 | ///
72 | /// The full key entry string.
73 | public string GetFullKeyEntry => $"{KeyTypeDeclarationInFile} {Fingerprint} {Comment}";
74 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Credentials/ConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:31
6 |
7 | #endregion
8 |
9 | using System.Text.Json.Serialization;
10 | using OpenSSH_GUI.Core.Enums;
11 | using OpenSSH_GUI.Core.Interfaces.Credentials;
12 | using Renci.SshNet;
13 |
14 | namespace OpenSSH_GUI.Core.Lib.Credentials;
15 |
16 | ///
17 | /// Represents the base class for connection credentials.
18 | ///
19 | public class ConnectionCredentials(string hostname, string username, AuthType authType)
20 | : IConnectionCredentials
21 | {
22 | ///
23 | /// Represents the unique identifier for a connection credentials object.
24 | /// This property is used in classes related to connection credentials and server settings.
25 | ///
26 | public int Id { get; set; }
27 |
28 | ///
29 | /// Represents the hostname of a server.
30 | /// This property is used in classes related to connection credentials and server settings.
31 | ///
32 | public string Hostname { get; set; } = hostname;
33 |
34 | ///
35 | /// Represents the port number used for establishing an SSH connection.
36 | ///
37 | public int Port => Hostname.Contains(':') ? int.Parse(Hostname.Split(':')[1]) : 22;
38 |
39 | ///
40 | /// Represents the username property of a connection credentials.
41 | ///
42 | public string Username { get; set; } = username;
43 |
44 | ///
45 | /// Retrieves the connection information based on the provided credentials.
46 | ///
47 | ///
48 | /// The object representing the SSH connection information.
49 | ///
50 | public virtual ConnectionInfo GetConnectionInfo()
51 | {
52 | return new ConnectionInfo(Hostname, Username);
53 | }
54 |
55 | ///
56 | /// Gets the display string for the connection credentials.
57 | ///
58 | [JsonIgnore]
59 | public string Display => ToString();
60 |
61 | ///
62 | /// Represents the authentication type for a connection.
63 | ///
64 | public AuthType AuthType { get; set; } = authType;
65 |
66 | ///
67 | /// Returns a string representation of the ConnectionCredentials object.
68 | ///
69 | /// A string in the format "{Username}@{Hostname}:{Port}"
70 | public override string ToString()
71 | {
72 | return $"{Username}@{Hostname}{(Port is 22 ? "" : $":{Port}")}";
73 | }
74 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Credentials/KeyConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:30
6 |
7 | #endregion
8 |
9 | using System.Text.Json.Serialization;
10 | using OpenSSH_GUI.Core.Enums;
11 | using OpenSSH_GUI.Core.Interfaces.Credentials;
12 | using OpenSSH_GUI.Core.Interfaces.Keys;
13 | using OpenSSH_GUI.Core.Lib.Static;
14 | using Renci.SshNet;
15 |
16 | namespace OpenSSH_GUI.Core.Lib.Credentials;
17 |
18 | ///
19 | /// Represents the credentials for a key-based connection to a server.
20 | ///
21 | public class KeyConnectionCredentials : ConnectionCredentials, IKeyConnectionCredentials
22 | {
23 | ///
24 | /// Represents connection credentials using SSH key authentication.
25 | ///
26 | public KeyConnectionCredentials(string hostname, string username, ISshKey? key) : base(hostname, username,
27 | AuthType.Key)
28 | {
29 | Key = key;
30 | }
31 |
32 | ///
33 | /// Represents connection credentials that include an SSH key for authentication.
34 | ///
35 | [JsonIgnore]
36 | public ISshKey? Key { get; set; }
37 |
38 |
39 | ///
40 | /// Renews the SSH key used for authentication.
41 | ///
42 | /// The password for the key file (optional).
43 | public void RenewKey(string? password = null)
44 | {
45 | Key = KeyFactory.FromDtoId(Key.Id);
46 | }
47 |
48 | ///
49 | /// Retrieves the connection information based on the provided credentials.
50 | ///
51 | ///
52 | /// The object representing the SSH connection information.
53 | ///
54 | public override ConnectionInfo GetConnectionInfo()
55 | {
56 | return new PrivateKeyConnectionInfo(Hostname, Username, ProxyTypes.None, "", 0, Key.GetSshNetKeyType());
57 | }
58 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Credentials/MultiKeyConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:31
6 |
7 | #endregion
8 |
9 | using System.Text.Json.Serialization;
10 | using OpenSSH_GUI.Core.Enums;
11 | using OpenSSH_GUI.Core.Interfaces.Credentials;
12 | using OpenSSH_GUI.Core.Interfaces.Keys;
13 | using Renci.SshNet;
14 |
15 | namespace OpenSSH_GUI.Core.Lib.Credentials;
16 |
17 | /// *MultiKeyConnectionCredentials(string hostname, string username,
18 | ///
19 | /// ? keys)**
20 | public class MultiKeyConnectionCredentials : ConnectionCredentials, IMultiKeyConnectionCredentials
21 | {
22 | ///
23 | /// Represents a set of connection credentials for a multi-key authentication.
24 | ///
25 | public MultiKeyConnectionCredentials(string hostname, string username, IEnumerable? keys) : base(hostname,
26 | username, AuthType.MultiKey)
27 | {
28 | Keys = keys;
29 | }
30 |
31 | ///
32 | /// Represents the credentials for a multi-key SSH connection.
33 | ///
34 | [JsonIgnore]
35 | public IEnumerable? Keys { get; set; }
36 |
37 |
38 | ///
39 | /// Retrieves the connection information for establishing an SSH connection.
40 | ///
41 | ///
42 | /// The object representing the SSH connection information.
43 | ///
44 | public override ConnectionInfo GetConnectionInfo()
45 | {
46 | return new PrivateKeyConnectionInfo(Hostname, Port, Username, Keys.Select(e => e.GetSshNetKeyType()).ToArray());
47 | }
48 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Credentials/PasswordConnectionCredentials.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:31
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 | using OpenSSH_GUI.Core.Interfaces.Credentials;
11 | using Renci.SshNet;
12 |
13 | namespace OpenSSH_GUI.Core.Lib.Credentials;
14 |
15 | /// **hostname**: The hostname or IP address of the remote server.
16 | /// **username**: The username used to authenticate with the remote server.
17 | /// **password**: The password used to authenticate with the remote server.
18 | /// **encryptedPassword**: (optional) Specifies whether the password is encrypted. The default value is `false`.
19 | public class PasswordConnectionCredentials(
20 | string hostname,
21 | string username,
22 | string password,
23 | bool encryptedPassword = false)
24 | : ConnectionCredentials(hostname, username, AuthType.Password), IPasswordConnectionCredentials
25 | {
26 | ///
27 | /// Represents connection credentials using password authentication.
28 | ///
29 | public string Password { get; set; } = password;
30 |
31 | ///
32 | /// Gets or sets a value indicating whether the password is encrypted.
33 | ///
34 | ///
35 | /// true if the password is encrypted; otherwise, false.
36 | ///
37 | public bool EncryptedPassword { get; set; } = encryptedPassword;
38 |
39 | ///
40 | /// Retrieves the connection information based on the provided credentials.
41 | ///
42 | /// The object representing the SSH connection information.
43 | public override ConnectionInfo GetConnectionInfo()
44 | {
45 | return new PasswordConnectionInfo(Hostname, Username, Password);
46 | }
47 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Keys/SSHKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:25
6 |
7 | #endregion
8 |
9 | using System.Diagnostics;
10 | using OpenSSH_GUI.Core.Interfaces.Keys;
11 | using OpenSSH_GUI.Core.Lib.Abstract;
12 | using OpenSSH_GUI.Core.Lib.Static;
13 | using SshNet.Keygen;
14 |
15 | namespace OpenSSH_GUI.Core.Lib.Keys;
16 |
17 | ///
18 | /// Represents an SSH key.
19 | ///
20 | public abstract class SshKey : KeyBase, ISshKey
21 | {
22 | ///
23 | /// Represents a SSH key.
24 | ///
25 | protected SshKey(string absoluteFilePath, string? password = null) : base(absoluteFilePath, password)
26 | {
27 | if (!FileOperations.Exists(AbsoluteFilePath))
28 | throw new FileNotFoundException($"No such file: {AbsoluteFilePath}");
29 | var outputOfProcess = ReadSshFile(ref absoluteFilePath).Split(' ').ToList();
30 | outputOfProcess.RemoveRange(0, 2);
31 | outputOfProcess.Remove(outputOfProcess.Last());
32 | Comment = string.Join(" ", outputOfProcess);
33 | Format = SshKeyFormat.OpenSSH;
34 | }
35 |
36 | ///
37 | /// Gets the comment associated with the SSH key.
38 | ///
39 | /// The comment.
40 | public string Comment { get; }
41 |
42 |
43 | ///
44 | /// Gets a value indicating whether the key is a Putty key.
45 | ///
46 | public bool IsPuttyKey => Format is not SshKeyFormat.OpenSSH;
47 |
48 | ///
49 | /// Exports the text representation of the SSH key.
50 | ///
51 | /// The text representation of the SSH key.
52 | public override string ExportTextOfKey()
53 | {
54 | return this is ISshPublicKey ? ExportOpenSshPublicKey()! : ExportOpenSshPrivateKey()!;
55 | }
56 |
57 | ///
58 | /// Reads the contents of an SSH file.
59 | ///
60 | /// The absolute file path of the SSH file to read.
61 | /// The contents of the SSH file.
62 | private string ReadSshFile(ref string filePath)
63 | {
64 | using var readerProcess = new Process();
65 | readerProcess.StartInfo = new ProcessStartInfo
66 | {
67 | WindowStyle = ProcessWindowStyle.Hidden,
68 | RedirectStandardOutput = true,
69 | RedirectStandardError = true,
70 | CreateNoWindow = true,
71 | Arguments = $"-l -f {filePath}",
72 | FileName = "ssh-keygen"
73 | };
74 | readerProcess.Start();
75 | return readerProcess.StandardOutput.ReadToEnd();
76 | }
77 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Keys/SSHKeyType.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:27
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 | using OpenSSH_GUI.Core.Extensions;
11 | using OpenSSH_GUI.Core.Interfaces.Keys;
12 |
13 | namespace OpenSSH_GUI.Core.Lib.Keys;
14 |
15 | ///
16 | /// Represents an SSH key type.
17 | ///
18 | public class SshKeyType : ISshKeyType
19 | {
20 | ///
21 | /// Represents a SSH key type.
22 | ///
23 | public SshKeyType(KeyType baseType)
24 | {
25 | BaseType = baseType;
26 | KeyTypeText = Enum.GetName(BaseType)!;
27 | var possibleBitSizes = BaseType.GetBitValues().ToList();
28 | PossibleBitSizes = possibleBitSizes;
29 | HasDefaultBitSize = !PossibleBitSizes.Any();
30 | CurrentBitSize = HasDefaultBitSize ? 0 : PossibleBitSizes.Max();
31 | }
32 |
33 | public SshKeyType(string keyDefinition)
34 | {
35 | KeyTypeText = keyDefinition;
36 | BaseType = Enum.Parse(
37 | (KeyTypeText.StartsWith("ecdsa")
38 | ? KeyTypeText.Split('-')[0]
39 | : KeyTypeText.Split('-')[1]).ToUpper());
40 | var possibleBitSizes = BaseType.GetBitValues().ToList();
41 | PossibleBitSizes = possibleBitSizes;
42 | HasDefaultBitSize = !PossibleBitSizes.Any();
43 | CurrentBitSize = HasDefaultBitSize ? 0 : PossibleBitSizes.Max();
44 | }
45 |
46 | ///
47 | /// A class representing the base type of a SSH key.
48 | ///
49 | public KeyType BaseType { get; }
50 |
51 | public string KeyTypeText { get; }
52 |
53 | ///
54 | /// Gets a value indicating whether the SSH key type has a default bit size.
55 | ///
56 | ///
57 | /// The SSH key type may have a default bit size if there are no possible bit sizes defined for the key type.
58 | ///
59 | public bool HasDefaultBitSize { get; }
60 |
61 | ///
62 | /// Gets or sets the current bit size of the SSH key type.
63 | ///
64 | public int CurrentBitSize { get; set; }
65 |
66 | ///
67 | /// Represents a type of SSH key.
68 | ///
69 | public IEnumerable PossibleBitSizes { get; }
70 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Keys/SshPrivateKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:26
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Interfaces.Keys;
10 |
11 | namespace OpenSSH_GUI.Core.Lib.Keys;
12 |
13 | ///
14 | /// Represents an SSH private key.
15 | ///
16 | public class SshPrivateKey(string absoluteFilePath, string? password = null)
17 | : SshKey(absoluteFilePath, password), ISshPrivateKey;
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Keys/SshPublicKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:27
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Interfaces.Keys;
10 | using OpenSSH_GUI.Core.Lib.Static;
11 |
12 | namespace OpenSSH_GUI.Core.Lib.Keys;
13 |
14 | ///
15 | /// Represents an SSH public key.
16 | ///
17 | public class SshPublicKey(string absoluteFilePath, string? password = null)
18 | : SshKey(absoluteFilePath, password), ISshPublicKey
19 | {
20 | ///
21 | /// Represents a private key.
22 | ///
23 | public ISshKey PrivateKey { get; } = KeyFactory.FromPath(Path.ChangeExtension(absoluteFilePath, null), password)!;
24 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/KnownHosts/KnownHost.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:29
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Interfaces.KnownHosts;
10 | using ReactiveUI;
11 |
12 | namespace OpenSSH_GUI.Core.Lib.KnownHosts;
13 |
14 | ///
15 | /// Represents a known host in the OpenSSH GUI.
16 | ///
17 | public class KnownHost : ReactiveObject, IKnownHost
18 | {
19 | /// The _keys variable represents a collection of known host keys for a specific host.
20 | /// It is an instance variable of the KnownHost class.
21 | /// The _keys variable is a list of IKnownHostKey objects that store information about individual host keys.
22 | /// Each IKnownHostKey object represents a known host key and provides access to its properties such as key type, fingerprint, and deletion status.
23 | /// Usage:
24 | /// var knownHost = new KnownHost(knownHosts);
25 | /// knownHost.Keys = _keys; // Set the list of known host keys
26 | /// See IKnownHostKey and KnownHost classes for more information.
27 | /// /
28 | private List _keys = [];
29 |
30 | ///
31 | /// Represents a known host entry in the known_hosts file.
32 | ///
33 | public KnownHost(IGrouping knownHosts)
34 | {
35 | Host = knownHosts.Key;
36 | Keys = knownHosts.Select(e => new KnownHostKey(e.Replace($"{Host}", "").Trim()) as IKnownHostKey).ToList();
37 | }
38 |
39 | ///
40 | /// Gets or sets the toggled state of the switch.
41 | ///
42 | ///
43 | /// When the switch is toggled:
44 | /// - If it was previously off, all known host keys are marked for deletion.
45 | /// - If it was previously on, all known host keys are unmarked for deletion.
46 | ///
47 | private bool SwitchToggled { get; set; }
48 |
49 | ///
50 | /// Represents a known host in the SSH known hosts file.
51 | ///
52 | public string Host { get; }
53 |
54 | ///
55 | /// Represents a known host that can be deleted in its entirety.
56 | ///
57 | public bool DeleteWholeHost => Keys.All(e => e.MarkedForDeletion);
58 |
59 | ///
60 | /// Represents a known host in the OpenSSH_GUI.
61 | ///
62 | public List Keys
63 | {
64 | get => _keys;
65 | set => this.RaiseAndSetIfChanged(ref _keys, value);
66 | }
67 |
68 | ///
69 | /// Toggles the marked for deletion flag of each within the list.
70 | /// If the property is true, it sets the flag to false for all keys. Otherwise, it sets
71 | /// the flag to true for all keys.
72 | ///
73 | public void KeysDeletionSwitch()
74 | {
75 | if (SwitchToggled)
76 | {
77 | foreach (var key in Keys) key.MarkedForDeletion = false;
78 |
79 | SwitchToggled = false;
80 | }
81 | else
82 | {
83 | foreach (var key in Keys) key.MarkedForDeletion = true;
84 |
85 | SwitchToggled = true;
86 | }
87 | }
88 |
89 | ///
90 | /// Retrieves all entries for a known host in the known hosts file.
91 | ///
92 | ///
93 | /// Returns a string containing all the entries for the known host.
94 | /// If the entire host is marked for deletion, returns the line ending character.
95 | ///
96 | public string GetAllEntries()
97 | {
98 | return DeleteWholeHost
99 | ? IKnownHostsFile.LineEnding
100 | : Keys
101 | .Where(e => !e.MarkedForDeletion)
102 | .Aggregate("",
103 | (current, knownHostsKey) =>
104 | current + $"{Host} {knownHostsKey.EntryWithoutHost}{IKnownHostsFile.LineEnding}");
105 | }
106 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/KnownHosts/KnownHostKey.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:30
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Core.Enums;
10 | using OpenSSH_GUI.Core.Interfaces.KnownHosts;
11 | using ReactiveUI;
12 |
13 | namespace OpenSSH_GUI.Core.Lib.KnownHosts;
14 |
15 | ///
16 | /// Represents a known host key in the OpenSSH GUI.
17 | ///
18 | public class KnownHostKey : ReactiveObject, IKnownHostKey
19 | {
20 | ///
21 | /// Represents a known host key.
22 | ///
23 | private bool _markedForDeletion;
24 |
25 | ///
26 | /// Represents a known host key in the OpenSSH GUI.
27 | ///
28 | public KnownHostKey(string entry)
29 | {
30 | EntryWithoutHost = entry;
31 | var splitted = EntryWithoutHost.Split(' ');
32 | TypeDeclarationInFile = splitted[0];
33 | KeyType = Enum.Parse(
34 | TypeDeclarationInFile.StartsWith("ssh-")
35 | ? TypeDeclarationInFile.Replace("ssh-", "")
36 | : TypeDeclarationInFile.Split('-')[0], true);
37 | Fingerprint = splitted[1].Replace("\n", "").Replace("\r", "");
38 | }
39 |
40 | ///
41 | /// Represents a known host key.
42 | ///
43 | private string TypeDeclarationInFile { get; }
44 |
45 | ///
46 | /// Represents the type of a known host key.
47 | ///
48 | public KeyType KeyType { get; }
49 |
50 | ///
51 | /// Represents a known host key.
52 | ///
53 | public string Fingerprint { get; }
54 |
55 | ///
56 | /// Represents a known host key without the host entry in the OpenSSH GUI.
57 | ///
58 | public string EntryWithoutHost { get; }
59 |
60 | ///
61 | /// Gets or sets a value indicating whether the known host key is marked for deletion.
62 | ///
63 | public bool MarkedForDeletion
64 | {
65 | get => _markedForDeletion;
66 | set => this.RaiseAndSetIfChanged(ref _markedForDeletion, value);
67 | }
68 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Misc/DirectoryCrawler.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:27
6 |
7 | #endregion
8 |
9 | using Microsoft.EntityFrameworkCore;
10 | using Microsoft.Extensions.Logging;
11 | using OpenSSH_GUI.Core.Database.Context;
12 | using OpenSSH_GUI.Core.Database.DTO;
13 | using OpenSSH_GUI.Core.Extensions;
14 | using OpenSSH_GUI.Core.Interfaces.Keys;
15 | using OpenSSH_GUI.Core.Lib.Static;
16 |
17 | namespace OpenSSH_GUI.Core.Lib.Misc;
18 |
19 | ///
20 | /// Represents a directory crawler for searching and managing SSH keys.
21 | ///
22 | public static class DirectoryCrawler
23 | {
24 | private static ILogger _logger = null!;
25 |
26 | ///
27 | /// Provides the logger context for the DirectoryCrawler class.
28 | ///
29 | /// The logger instance to be used by the DirectoryCrawler class.
30 | public static void ProvideContext(ILogger logger)
31 | {
32 | _logger = logger;
33 | }
34 |
35 | ///
36 | /// Retrieves SSH keys from disk.
37 | ///
38 | ///
39 | /// Optional. Indicates whether to automatically convert PuTTY keys to OpenSSH format. Default is
40 | /// false.
41 | ///
42 | /// An enumerable collection of ISshKey representing the SSH keys.
43 | private static IEnumerable GetFromDisk(bool convert)
44 | {
45 | foreach (var filePath in Directory
46 | .EnumerateFiles(SshConfigFilesExtension.GetBaseSshPath(), "*", SearchOption.TopDirectoryOnly)
47 | .Where(e => e.EndsWith("pub") || e.EndsWith("ppk")))
48 | {
49 | ISshKey? key = null;
50 | try
51 | {
52 | key = KeyFactory.FromPath(filePath);
53 | if (key is IPpkKey && convert) key = KeyFactory.ConvertToOppositeFormat(key, true);
54 | }
55 | catch (Exception ex)
56 | {
57 | _logger.LogError(ex, "Error while reading key from {path}", filePath);
58 | }
59 |
60 | if (key is null) continue;
61 | yield return key;
62 | }
63 | }
64 |
65 | ///
66 | /// Retrieves all SSH keys from disk or cache asynchronously, using a yield return method to lazily load the keys.
67 | ///
68 | /// Optional. Indicates whether to load keys from disk. Default is false.
69 | /// Optional. Indicates whether to purge passwords from cache. Default is false.
70 | /// An asynchronous enumerable collection of ISshKey representing the SSH keys.
71 | public static async IAsyncEnumerable GetAllKeysYield(bool loadFromDisk = false,
72 | bool purgePasswords = false)
73 | {
74 | await using var dbContext = new OpenSshGuiDbContext();
75 | var cacheHasElements = await dbContext.KeyDtos.AnyAsync();
76 | switch (loadFromDisk)
77 | {
78 | case false when cacheHasElements:
79 | foreach (var keyFromCache in dbContext.KeyDtos.Select(e => e.ToKey())) yield return keyFromCache!;
80 | yield break;
81 | case false when !cacheHasElements:
82 | loadFromDisk = true;
83 | break;
84 | }
85 |
86 | if (!loadFromDisk && cacheHasElements) yield break;
87 | {
88 | foreach (var key in GetFromDisk((await dbContext.Settings.FirstAsync()).ConvertPpkAutomatically))
89 | {
90 | var found = await dbContext.KeyDtos.FirstOrDefaultAsync(e =>
91 | e.AbsolutePath == key.AbsoluteFilePath);
92 | if (found is null)
93 | {
94 | await dbContext.KeyDtos.AddAsync(new SshKeyDto
95 | {
96 | AbsolutePath = key.AbsoluteFilePath,
97 | Password = key.Password,
98 | Format = key.Format
99 | });
100 | }
101 | else
102 | {
103 | found.AbsolutePath = key.AbsoluteFilePath;
104 | found.Format = key.Format;
105 | if (found.Password is not null) key.Password = found.Password;
106 | if (purgePasswords)
107 | {
108 | key.Password = key.HasPassword ? "" : null;
109 | found.Password = key.HasPassword ? "" : null;
110 | }
111 | }
112 |
113 | await dbContext.SaveChangesAsync();
114 | yield return key;
115 | }
116 | }
117 | }
118 |
119 | ///
120 | /// Retrieves all SSH keys from disk or cache.
121 | ///
122 | /// Optional. Indicates whether to load keys from disk. Default is false.
123 | /// An enumerable collection of ISshKey representing the SSH keys.
124 | public static IEnumerable GetAllKeys(bool loadFromDisk = false)
125 | {
126 | return GetAllKeysYield(loadFromDisk).ToBlockingEnumerable();
127 | }
128 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Misc/SshKeyGenerateParams.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 18.05.2024 - 13:05:38
3 | // Last edit: 18.05.2024 - 13:05:38
4 |
5 | using OpenSSH_GUI.Core.Enums;
6 | using OpenSSH_GUI.Core.Extensions;
7 | using SshNet.Keygen;
8 |
9 | namespace OpenSSH_GUI.Core.Lib.Misc;
10 |
11 | public readonly record struct SshKeyGenerateParams
12 | {
13 | public SshKeyGenerateParams(KeyType type,
14 | SshKeyFormat format,
15 | string? fileName = null,
16 | string? filePath = null,
17 | string? password = null,
18 | string? comment = null,
19 | int? keyLength = null)
20 | {
21 | FileName = fileName ?? Path.ChangeExtension(Path.GetFileName("id_" + Path.GetTempFileName()), null);
22 | FilePath = filePath ?? SshConfigFilesExtension.GetBaseSshPath();
23 | KeyType = type;
24 | Comment = comment ?? "by OpenSSH-GUI";
25 | KeyFormat = format;
26 | KeyLength = keyLength ?? (int)type;
27 | Password = password;
28 | }
29 |
30 | ///
31 | /// Gets or sets the file name for the SSH key.
32 | ///
33 | public string FileName { get; }
34 |
35 | ///
36 | /// Represents the file path for an SSH key.
37 | ///
38 | /// Defaults to ~/.shh directory when null
39 | public string FilePath { get; }
40 |
41 | ///
42 | /// Represents the type of SSH key.
43 | ///
44 | public KeyType KeyType { get; }
45 |
46 | ///
47 | /// Represents a comment associated with an SSH key.
48 | ///
49 | /// Defaults to "by OpenSSH-GUI" when null
50 | public string Comment { get; }
51 |
52 | ///
53 | /// Specifies the format for SSH key generation.
54 | ///
55 | public SshKeyFormat KeyFormat { get; }
56 |
57 | ///
58 | /// Represents the length of an SSH key.
59 | ///
60 | public int KeyLength { get; }
61 |
62 | ///
63 | /// Represents the password associated with an SSH key.
64 | ///
65 | public string? Password { get; }
66 |
67 | public string FullFilePath => Path.Combine(FilePath, FileName);
68 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Settings/Settings.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:29
6 |
7 | #endregion
8 |
9 | using System.Reflection;
10 |
11 | namespace OpenSSH_GUI.Core.Lib.Settings;
12 |
13 | ///
14 | /// Represents a settings file for the application.
15 | ///
16 | public record Settings
17 | {
18 | ///
19 | /// The current version of the application.
20 | ///
21 | public string Version { get; set; } =
22 | $"v{(Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly()).GetName().Version?.ToString(3)}";
23 |
24 | ///
25 | /// Whether to convert ppk files automatically.
26 | ///
27 | public bool ConvertPpkAutomatically { get; set; } = false;
28 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/Lib/Static/FileOperations.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 30.05.2024 - 12:05:06
5 | // Last edit: 30.05.2024 - 12:05:06
6 |
7 | #endregion
8 |
9 | using Microsoft.Extensions.Logging;
10 | using Microsoft.Extensions.Logging.Abstractions;
11 | using OpenSSH_GUI.Core.Enums;
12 | using OpenSSH_GUI.Core.Extensions;
13 |
14 | namespace OpenSSH_GUI.Core.Lib.Static;
15 |
16 | public static class FileOperations
17 | {
18 | public static void EnsureFilesAndFoldersExist(ILogger? logger = null)
19 | {
20 | logger??= NullLogger.Instance;
21 | SshConfigFilesExtension.ValidateDirectories(logger);
22 | foreach (var configFile in Enum.GetValues())
23 | {
24 | var pathOfFile = configFile.GetPathOfFile();
25 | try
26 | {
27 | if (!File.Exists(pathOfFile)) OpenOrCreate(pathOfFile);
28 | }
29 | catch (Exception e)
30 | {
31 | logger.LogError(e, "Error creating file {pathOfFile}", pathOfFile);
32 | }
33 | }
34 | }
35 |
36 | private static FileStreamOptions Options(FileMode mode)
37 | {
38 | var options = new FileStreamOptions
39 | {
40 | Access = FileAccess.ReadWrite,
41 | Mode = mode,
42 | Share = FileShare.ReadWrite
43 | };
44 | if (Environment.OSVersion.Platform is PlatformID.Unix or PlatformID.MacOSX)
45 | {
46 | #pragma warning disable CA1416
47 | options.UnixCreateMode = mode
48 | is FileMode.Create
49 | or FileMode.CreateNew
50 | or FileMode.OpenOrCreate
51 | ? UnixFileMode.UserRead | UnixFileMode.UserWrite
52 | : null;
53 | #pragma warning restore CA1416
54 | }
55 |
56 | return options;
57 | }
58 |
59 | public static FileStream DeleteOldAndCreateNew(string filePath)
60 | {
61 | if (Exists(filePath)) Delete(filePath);
62 | return OpenOrCreate(filePath);
63 | }
64 |
65 | public static FileStream OpenOrCreate(string filePath)
66 | {
67 | return new FileStream(filePath, Options(FileMode.OpenOrCreate));
68 | }
69 |
70 | public static FileStream OpenTruncated(string filePath)
71 | {
72 | return File.Open(filePath, Options(FileMode.Truncate));
73 | }
74 |
75 | public static string[] ReadAllLines(string filePath)
76 | {
77 | return File.ReadAllLines(filePath);
78 | }
79 |
80 | public static bool Exists(string filePath)
81 | {
82 | return File.Exists(filePath);
83 | }
84 |
85 | public static void Move(string source, string destination, bool overwrite = false)
86 | {
87 | File.Move(source, destination, overwrite);
88 | }
89 |
90 | public static void Delete(string filePath)
91 | {
92 | if (File.Exists(filePath)) File.Delete(filePath);
93 | }
94 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.Core/OpenSSH_GUI.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | 2.0.0
8 | 2.0.0
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/OpenSSH_GUI.Tests/OpenSSH_GUI.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | OpenSSHA_Tests
6 | enable
7 | enable
8 |
9 | false
10 | true
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 | all
22 | runtime; build; native; contentfiles; analyzers; buildtransitive
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/OpenSSH_GUI.Tests/UnitTest1.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:35
6 |
7 | #endregion
8 |
9 | namespace OpenSSHA_Tests;
10 |
11 | public class Tests
12 | {
13 | [SetUp]
14 | public void Setup()
15 | {
16 | }
17 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSSH_GUI", "OpenSSH_GUI\OpenSSH_GUI.csproj", "{833C761B-4BDF-402A-9B27-8995E2AD1DE3}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSSH_GUI.Core", "OpenSSH_GUI.Core\OpenSSH_GUI.Core.csproj", "{08608099-A6CD-405A-A3F0-70934D94D519}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSSH_GUI.Tests", "OpenSSH_GUI.Tests\OpenSSH_GUI.Tests.csproj", "{5B020E47-0F52-49C5-8D9F-23184B795186}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {833C761B-4BDF-402A-9B27-8995E2AD1DE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {833C761B-4BDF-402A-9B27-8995E2AD1DE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {833C761B-4BDF-402A-9B27-8995E2AD1DE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {833C761B-4BDF-402A-9B27-8995E2AD1DE3}.Release|Any CPU.Build.0 = Release|Any CPU
19 | {833C761B-4BDF-402A-9B27-8995E2AD1DE3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
20 | {08608099-A6CD-405A-A3F0-70934D94D519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {08608099-A6CD-405A-A3F0-70934D94D519}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {08608099-A6CD-405A-A3F0-70934D94D519}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {08608099-A6CD-405A-A3F0-70934D94D519}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {5B020E47-0F52-49C5-8D9F-23184B795186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {5B020E47-0F52-49C5-8D9F-23184B795186}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {5B020E47-0F52-49C5-8D9F-23184B795186}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {5B020E47-0F52-49C5-8D9F-23184B795186}.Release|Any CPU.Build.0 = Release|Any CPU
28 | EndGlobalSection
29 | EndGlobal
30 |
--------------------------------------------------------------------------------
/OpenSSH_GUI.sln.DotSettings.user:
--------------------------------------------------------------------------------
1 |
2 |
3 | True
4 | False
5 | True
6 | False
7 | True
8 |
9 |
10 |
11 |
12 |
13 |
14 | False
15 | True
16 | True
17 |
18 | True
19 | False
20 | False
21 | False
22 | False
--------------------------------------------------------------------------------
/OpenSSH_GUI/App.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/OpenSSH_GUI/App.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:35
6 |
7 | #endregion
8 |
9 | using System;
10 | using System.IO;
11 | using System.Linq;
12 | using Avalonia;
13 | using Avalonia.Controls;
14 | using Avalonia.Controls.ApplicationLifetimes;
15 | using Avalonia.Markup.Xaml;
16 | using Avalonia.Media.Imaging;
17 | using Avalonia.Platform;
18 | using Microsoft.EntityFrameworkCore;
19 | using Microsoft.Extensions.DependencyInjection;
20 | using Microsoft.Extensions.Logging;
21 | using OpenSSH_GUI.Core.Database.Context;
22 | using OpenSSH_GUI.Core.Extensions;
23 | using OpenSSH_GUI.Core.Lib.Misc;
24 | using OpenSSH_GUI.Core.Lib.Settings;
25 | using OpenSSH_GUI.Core.Lib.Static;
26 | using OpenSSH_GUI.ViewModels;
27 | using OpenSSH_GUI.Views;
28 | using Serilog;
29 | using Serilog.Events;
30 |
31 | namespace OpenSSH_GUI;
32 |
33 | public class App : Application
34 | {
35 | public static ServiceProvider ServiceProvider { get; private set; }
36 | public static WindowIcon WindowIcon => new (new Bitmap(AssetLoader.Open(new Uri("avares://OpenSSH_GUI/Assets/appicon.ico"))));
37 |
38 | public override void Initialize()
39 | {
40 | AvaloniaXamlLoader.Load(this);
41 | }
42 |
43 | public override void OnFrameworkInitializationCompleted()
44 | {
45 | ServiceProvider = BuildServiceCollection().BuildServiceProvider();
46 | using (var db = new OpenSshGuiDbContext())
47 | {
48 | db.Database.Migrate();
49 | if (!db.Settings.Any()) db.Settings.Add(new Settings());
50 | db.SaveChanges();
51 | }
52 |
53 | InitAndOrPrepareServices();
54 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
55 | desktop.MainWindow = new MainWindow
56 | {
57 | DataContext = new MainWindowViewModel()
58 | };
59 | base.OnFrameworkInitializationCompleted();
60 | }
61 |
62 | private void InitAndOrPrepareServices()
63 | {
64 | var logger = ServiceProvider.GetRequiredService>();
65 | DirectoryCrawler.ProvideContext(logger);
66 | FileOperations.EnsureFilesAndFoldersExist(logger);
67 | }
68 |
69 | private ServiceCollection BuildServiceCollection()
70 | {
71 | var collection = new ServiceCollection();
72 | var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
73 | AppDomain.CurrentDomain.FriendlyName);
74 | var logFilebasePath = Path.Combine(appDataPath, "logs");
75 | if (!Directory.Exists(logFilebasePath)) Directory.CreateDirectory(logFilebasePath);
76 | var logFilePath = Path.Combine(logFilebasePath, $"{AppDomain.CurrentDomain.FriendlyName}.log");
77 | var serilog = new LoggerConfiguration()
78 | .Enrich.FromLogContext()
79 | .WriteTo.File(logFilePath, LogEventLevel.Debug, rollingInterval: RollingInterval.Day)
80 | .CreateLogger();
81 | // AddServices
82 |
83 | collection.AddLogging(e => e.AddSerilog(serilog, true));
84 |
85 | // return ServiceCollection
86 | return collection;
87 | }
88 |
89 | private void CloseProgram(object? sender, EventArgs e)
90 | {
91 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) desktop.Shutdown();
92 | }
93 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Assets/appicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/OpenSSH_GUI/Assets/appicon.ico
--------------------------------------------------------------------------------
/OpenSSH_GUI/Assets/appicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/OpenSSH_GUI/Assets/appicon.png
--------------------------------------------------------------------------------
/OpenSSH_GUI/Assets/avalonia-logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/OpenSSH_GUI/Assets/avalonia-logo.ico
--------------------------------------------------------------------------------
/OpenSSH_GUI/Converters/PlatformIdConverter.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:40
6 |
7 | #endregion
8 |
9 | using System;
10 | using System.Globalization;
11 | using Avalonia.Data.Converters;
12 |
13 | namespace OpenSSH_GUI.Converters;
14 |
15 | public class PlatformIdConverter : IValueConverter
16 | {
17 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
18 | {
19 | if (Enum.GetName((PlatformID)value).Contains("Win", StringComparison.CurrentCultureIgnoreCase))
20 | return "Windows";
21 | return value is null ? value : Enum.GetName((PlatformID)value);
22 | }
23 |
24 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
25 | {
26 | return value is null ? value : Enum.Parse((string)value);
27 | }
28 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Converters/SingleSshKeyTypeConverter.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:41
6 |
7 | #endregion
8 |
9 | using System;
10 | using System.Globalization;
11 | using Avalonia.Data.Converters;
12 | using OpenSSH_GUI.Core.Enums;
13 |
14 | namespace OpenSSH_GUI.Converters;
15 |
16 | public class SingleSshKeyTypeConverter : IValueConverter
17 | {
18 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
19 | {
20 | return value is not null ? Enum.GetName(typeof(KeyType), (KeyType)value) : "";
21 | }
22 |
23 | public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
24 | {
25 | return value is not null ? Enum.Parse((string)value) : "";
26 | }
27 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Converters/SshKeyTypeConverter.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:40
6 |
7 | #endregion
8 |
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Globalization;
12 | using System.Linq;
13 | using Avalonia.Data.Converters;
14 | using OpenSSH_GUI.Core.Enums;
15 | using OpenSSH_GUI.Core.Interfaces.Keys;
16 | using OpenSSH_GUI.Core.Lib.Keys;
17 |
18 | namespace OpenSSH_GUI.Converters;
19 |
20 | public class SshKeyTypeConverter : IValueConverter
21 | {
22 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
23 | {
24 | if (value is not IEnumerable && value is ISshKeyType type) return type.BaseType;
25 | return (value as IEnumerable)!.Select(e => e.BaseType);
26 | }
27 |
28 | public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
29 | {
30 | if (value is not IEnumerable && value is KeyType type) return new SshKeyType(type);
31 | return (value as IEnumerable)!.Select(e => new SshKeyType(e));
32 | }
33 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/OpenSSH_GUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net8.0
5 | enable
6 | true
7 | app.manifest
8 | true
9 | true
10 | true
11 | true
12 | Assets\appicon.ico
13 | false
14 | frequency403
15 | 2.0.0
16 | 2.0.0
17 | en
18 | OpenSSH-GUI.snk
19 | false
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | all
32 | runtime; build; native; contentfiles; analyzers; buildtransitive
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | PublicResXFileCodeGenerator
49 | StringsAndTexts.Designer.cs
50 |
51 |
52 |
53 |
54 |
55 | True
56 | True
57 | StringsAndTexts.resx
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | OpenSSH GUI
67 | OpenSSH GUI
68 | frequency403.opensshgui
69 | 1.0.0
70 | APPL
71 | OpenSSHGui
72 | OpenSSHGui.icns
73 | NSApplication
74 | true
75 |
76 |
77 |
--------------------------------------------------------------------------------
/OpenSSH_GUI/Program.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:36
6 |
7 | #endregion
8 |
9 | using System;
10 | using Avalonia;
11 | using Avalonia.ReactiveUI;
12 |
13 | namespace OpenSSH_GUI;
14 |
15 | internal sealed class Program
16 | {
17 | // Initialization code. Don't use any Avalonia, third-party APIs or any
18 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
19 | // yet and stuff might break.
20 | [STAThread]
21 | public static void Main(string[] args)
22 | {
23 | BuildAvaloniaApp()
24 | .StartWithClassicDesktopLifetime(args);
25 | }
26 |
27 | // Avalonia configuration, don't remove; also used by visual designer.
28 | private static AppBuilder BuildAvaloniaApp()
29 | {
30 | return AppBuilder.Configure()
31 | .UsePlatformDetect()
32 | .WithInterFont()
33 | .LogToTrace()
34 | .UseReactiveUI();
35 | }
36 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Resources/Controls/BottomButtons.axaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
23 |
35 |
36 |
--------------------------------------------------------------------------------
/OpenSSH_GUI/Resources/Wrapper/WindowBase.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 27.05.2024 - 08:05:50
3 | // Last edit: 27.05.2024 - 08:05:51
4 |
5 | using System;
6 | using Avalonia.Controls;
7 | using Avalonia.Media.Imaging;
8 | using Avalonia.ReactiveUI;
9 | using OpenSSH_GUI.ViewModels;
10 | using ReactiveUI;
11 |
12 | namespace OpenSSH_GUI.Resources.Wrapper;
13 |
14 | public class WindowBase : ReactiveWindow where T : ViewModelBase
15 | {
16 |
17 | protected WindowBase()
18 | {
19 | Icon = App.WindowIcon;
20 | this.WhenActivated(d =>
21 | {
22 | d(ViewModel!.Submit.Subscribe(Close));
23 | d(ViewModel!.BooleanSubmit.Subscribe(Close));
24 | });
25 | }
26 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Resources/Wrapper/WindowInteraction.cs:
--------------------------------------------------------------------------------
1 | // File Created by: Oliver Schantz
2 | // Created: 21.05.2024 - 11:05:46
3 | // Last edit: 21.05.2024 - 11:05:47
4 |
5 | using System.Threading.Tasks;
6 | using Avalonia.Controls;
7 | using Avalonia.ReactiveUI;
8 | using OpenSSH_GUI.ViewModels;
9 | using OpenSSH_GUI.Views;
10 | using ReactiveUI;
11 |
12 | namespace OpenSSH_GUI.Resources.Wrapper;
13 |
14 | public static class WindowInteraction
15 | {
16 | public static async Task DialogMainWindow(
17 | IInteractionContext interaction, MainWindow windowOwner)
18 | where TViewModel : ViewModelBase, new()
19 | where TWindow : ReactiveWindow, new()
20 | {
21 | await DialogAnyWindow(interaction, windowOwner);
22 | }
23 |
24 | public static async Task DialogAnyWindow(
25 | IInteractionContext interaction,
26 | TWindowOwner windowOwner)
27 | where T : ViewModelBase, new()
28 | where TWindow : ReactiveWindow, new()
29 | where TWindowViewModel : ViewModelBase, new()
30 | where TWindowOwner : ReactiveWindow
31 |
32 | {
33 | var dialog = new TWindow
34 | {
35 | ViewModel = interaction.Input,
36 | WindowStartupLocation = WindowStartupLocation.CenterScreen
37 | };
38 | var result = await dialog.ShowDialog(windowOwner);
39 | interaction.SetOutput(result);
40 | }
41 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewLocator.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:36
6 |
7 | #endregion
8 |
9 | using System;
10 | using Avalonia.Controls;
11 | using Avalonia.Controls.Templates;
12 | using OpenSSH_GUI.ViewModels;
13 |
14 | namespace OpenSSH_GUI;
15 |
16 | public class ViewLocator : IDataTemplate
17 | {
18 | public Control? Build(object? data)
19 | {
20 | if (data is null)
21 | return null;
22 |
23 | var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
24 | var type = Type.GetType(name);
25 |
26 | if (type != null)
27 | {
28 | var control = (Control)Activator.CreateInstance(type)!;
29 | control.DataContext = data;
30 | return control;
31 | }
32 |
33 | return new TextBlock { Text = "Not Found: " + name };
34 | }
35 |
36 | public bool Match(object? data)
37 | {
38 | return data is ViewModelBase;
39 | }
40 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewModels/AddKeyWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:42
6 |
7 | #endregion
8 |
9 | using System;
10 | using System.Collections.ObjectModel;
11 | using System.Diagnostics;
12 | using System.IO;
13 | using System.Linq;
14 | using System.Threading.Tasks;
15 | using Microsoft.Extensions.Logging;
16 | using OpenSSH_GUI.Core.Extensions;
17 | using OpenSSH_GUI.Core.Interfaces.Keys;
18 | using OpenSSH_GUI.Core.Lib.Misc;
19 | using OpenSSH_GUI.Core.Lib.Static;
20 | using ReactiveUI;
21 | using ReactiveUI.Validation.Abstractions;
22 | using ReactiveUI.Validation.Contexts;
23 | using ReactiveUI.Validation.Extensions;
24 | using ReactiveUI.Validation.Helpers;
25 | using SshNet.Keygen;
26 |
27 | namespace OpenSSH_GUI.ViewModels;
28 |
29 | public sealed class AddKeyWindowViewModel : ViewModelBase, IValidatableViewModel
30 | {
31 | private readonly ValidationHelper _keyNameValidationHelper = new(new ValidationContext());
32 | private bool _createKey;
33 |
34 | private SshKeyFormat _keyFormat = SshKeyFormat.OpenSSH;
35 |
36 | private string _keyName = "id_rsa";
37 |
38 | private ISshKeyType _selectedKeyType;
39 |
40 | private ObservableCollection _sshKeyTypes;
41 |
42 | public AddKeyWindowViewModel()
43 | {
44 | KeyNameValidationHelper = this.ValidationRule(
45 | e => e.KeyName,
46 | name => !FileOperations.Exists(Path.Combine(SshConfigFilesExtension.GetBaseSshPath(), name)),
47 | StringsAndTexts.AddKeyWindowFilenameError
48 | );
49 | BooleanSubmit = ReactiveCommand.Create(b =>
50 | {
51 | _createKey = b;
52 | if (!_createKey) return null;
53 | return !FileOperations.Exists(SshConfigFilesExtension.GetBaseSshPath() + Path.DirectorySeparatorChar +
54 | KeyName)
55 | ? this
56 | : null;
57 | });
58 | _sshKeyTypes = new ObservableCollection(KeyTypeExtension.GetAvailableKeyTypes());
59 | _selectedKeyType = _sshKeyTypes.First();
60 | }
61 |
62 | public ValidationHelper KeyNameValidationHelper
63 | {
64 | get => _keyNameValidationHelper;
65 | private init => this.RaiseAndSetIfChanged(ref _keyNameValidationHelper, value);
66 | }
67 |
68 | public ISshKeyType SelectedKeyType
69 | {
70 | get => _selectedKeyType;
71 | set
72 | {
73 | try
74 | {
75 | KeyName = $"id_{Enum.GetName(value.BaseType)!.ToLower()}";
76 | this.RaiseAndSetIfChanged(ref _selectedKeyType, value);
77 | }
78 | catch (Exception e)
79 | {
80 | Debug.WriteLine(e);
81 | }
82 | }
83 | }
84 |
85 | public ObservableCollection SshKeyTypes
86 | {
87 | get => _sshKeyTypes;
88 | set => this.RaiseAndSetIfChanged(ref _sshKeyTypes, value);
89 | }
90 |
91 | public SshKeyFormat KeyFormat
92 | {
93 | get => _keyFormat;
94 | set => this.RaiseAndSetIfChanged(ref _keyFormat, value);
95 | }
96 |
97 | public SshKeyFormat[] SshKeyFormats { get; } = Enum.GetValues();
98 |
99 |
100 | public string KeyName
101 | {
102 | get => _keyName;
103 | set => this.RaiseAndSetIfChanged(ref _keyName, value);
104 | }
105 |
106 | public string Comment { get; set; } = $"{Environment.UserName}@{Environment.MachineName}";
107 | public string Password { get; set; } = "";
108 |
109 | public IValidationContext ValidationContext { get; } = new ValidationContext();
110 |
111 | public async ValueTask RunKeyGen()
112 | {
113 | try
114 | {
115 | return await KeyFactory.GenerateNewAsync(new SshKeyGenerateParams(
116 | SelectedKeyType.BaseType,
117 | KeyFormat,
118 | string.IsNullOrWhiteSpace(KeyName) ? null : KeyName,
119 | null,
120 | string.IsNullOrWhiteSpace(Password) ? null : Password,
121 | string.IsNullOrWhiteSpace(Comment) ? null : Comment,
122 | SelectedKeyType.CurrentBitSize
123 | ));
124 | }
125 | catch (Exception e)
126 | {
127 | Logger.LogError(e, "Error creating key");
128 | return null;
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewModels/ApplicationSettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:41
6 |
7 | #endregion
8 |
9 | using System.Collections.Generic;
10 | using System.Collections.ObjectModel;
11 | using System.Linq;
12 | using System.Reactive.Linq;
13 | using Microsoft.EntityFrameworkCore;
14 | using MsBox.Avalonia;
15 | using OpenSSH_GUI.Core.Database.Context;
16 | using OpenSSH_GUI.Core.Interfaces.Credentials;
17 | using OpenSSH_GUI.Core.Interfaces.Keys;
18 | using OpenSSH_GUI.Core.Lib.Settings;
19 | using ReactiveUI;
20 |
21 | namespace OpenSSH_GUI.ViewModels;
22 |
23 | public sealed class ApplicationSettingsViewModel : ViewModelBase
24 | {
25 | private bool _convertPpkAutomatically;
26 |
27 | private List _knownServers;
28 |
29 | private readonly Settings _settings;
30 |
31 | public ObservableCollection Keys = [];
32 | public Interaction ShowEditEntry = new();
33 |
34 | public ApplicationSettingsViewModel(ref ObservableCollection sshKeys)
35 | {
36 | Keys = sshKeys;
37 | using var dbContext = new OpenSshGuiDbContext();
38 | _settings = dbContext.Settings.First();
39 | _convertPpkAutomatically = _settings.ConvertPpkAutomatically;
40 | _knownServers = dbContext.ConnectionCredentialsDtos.Select(e => e.ToCredentials()).ToList();
41 |
42 | BooleanSubmit = ReactiveCommand.CreateFromTask(async e =>
43 | {
44 | if (!e) return this;
45 | await using var context = new OpenSshGuiDbContext();
46 | var file = context.Settings.Update(_settings).Entity;
47 | file.ConvertPpkAutomatically = ConvertPpkAutomatically;
48 | var knownServerIds = KnownServers.Select(s => s.Id).ToList();
49 | await context.ConnectionCredentialsDtos.Where(f => !knownServerIds.Contains(f.Id)).ExecuteDeleteAsync();
50 | await context.SaveChangesAsync();
51 | return this;
52 | });
53 | }
54 |
55 |
56 | public List KnownServers
57 | {
58 | get => _knownServers;
59 | set => this.RaiseAndSetIfChanged(ref _knownServers, value);
60 | }
61 |
62 | public bool ConvertPpkAutomatically
63 | {
64 | get => _convertPpkAutomatically;
65 | set => this.RaiseAndSetIfChanged(ref _convertPpkAutomatically, value);
66 | }
67 |
68 | public ReactiveCommand RemoveServer =>
69 | ReactiveCommand.Create(input =>
70 | {
71 | var index = KnownServers.IndexOf(input);
72 | var copy = KnownServers.ToList();
73 | copy.RemoveAt(index);
74 | KnownServers = copy;
75 | return this;
76 | });
77 |
78 | public ReactiveCommand EditEntry =>
79 | ReactiveCommand.CreateFromTask(
80 | async e =>
81 | {
82 | if (e is IMultiKeyConnectionCredentials)
83 | {
84 | var box = MessageBoxManager.GetMessageBoxStandard(
85 | StringsAndTexts.ApplicationSettingsEditErrorBoxTitle,
86 | StringsAndTexts.ApplicationSettingsEditErrorBoxText);
87 | await box.ShowAsync();
88 | return null;
89 | }
90 |
91 | var service = new EditSavedServerEntryViewModel();
92 | service.SetValues(ref Keys, e);
93 | var result = await ShowEditEntry.Handle(service);
94 | if (result is null) return e;
95 | var list = KnownServers.ToList();
96 | var index = list.IndexOf(e);
97 | list.RemoveAt(index);
98 | list.Insert(index, result.CredentialsToEdit);
99 | KnownServers = list;
100 | return result.CredentialsToEdit; // @TODO DoesNotShowKeys
101 | });
102 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewModels/EditAuthorizedKeysViewModel.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:43
6 |
7 | #endregion
8 |
9 | using System.Collections.ObjectModel;
10 | using System.Linq;
11 | using OpenSSH_GUI.Core.Enums;
12 | using OpenSSH_GUI.Core.Extensions;
13 | using OpenSSH_GUI.Core.Interfaces.AuthorizedKeys;
14 | using OpenSSH_GUI.Core.Interfaces.Keys;
15 | using OpenSSH_GUI.Core.Interfaces.Misc;
16 | using OpenSSH_GUI.Core.Lib.AuthorizedKeys;
17 | using ReactiveUI;
18 |
19 | namespace OpenSSH_GUI.ViewModels;
20 |
21 | public class EditAuthorizedKeysViewModel : ViewModelBase
22 | {
23 | private bool _addButtonEnabled;
24 |
25 | private ObservableCollection _publicKeys;
26 |
27 | private ISshKey? _selectedKey;
28 |
29 | private IServerConnection _serverConnection;
30 |
31 | public bool AddButtonEnabled
32 | {
33 | get => _addButtonEnabled;
34 | set => this.RaiseAndSetIfChanged(ref _addButtonEnabled, value);
35 | }
36 |
37 | public ISshKey? SelectedKey
38 | {
39 | get => _selectedKey;
40 | set
41 | {
42 | this.RaiseAndSetIfChanged(ref _selectedKey, value);
43 | UpdateAddButton();
44 | }
45 | }
46 |
47 | public ObservableCollection PublicKeys
48 | {
49 | get => _publicKeys;
50 | set => this.RaiseAndSetIfChanged(ref _publicKeys, value);
51 | }
52 |
53 | public bool KeyAddPossible => PublicKeys.Count > 0;
54 |
55 | public IServerConnection ServerConnection
56 | {
57 | get => _serverConnection;
58 | set => this.RaiseAndSetIfChanged(ref _serverConnection, value);
59 | }
60 |
61 | public IAuthorizedKeysFile AuthorizedKeysFileLocal { get; } =
62 | new AuthorizedKeysFile(SshConfigFiles.Authorized_Keys.GetPathOfFile());
63 |
64 | public IAuthorizedKeysFile AuthorizedKeysFileRemote { get; private set; }
65 | public ReactiveCommand AddKey { get; private set; }
66 |
67 | private void UpdateAddButton()
68 | {
69 | if (SelectedKey is null) return;
70 | AddButtonEnabled = !AuthorizedKeysFileRemote.AuthorizedKeys.Any(key =>
71 | string.Equals(key.Fingerprint, SelectedKey.ExportAuthorizedKey().Fingerprint));
72 | }
73 |
74 | public void SetConnectionAndKeys(ref IServerConnection serverConnection,
75 | ref ObservableCollection keys)
76 | {
77 | _serverConnection = serverConnection;
78 | AuthorizedKeysFileRemote = ServerConnection.GetAuthorizedKeysFromServer();
79 | _publicKeys = keys;
80 | _selectedKey = PublicKeys.FirstOrDefault();
81 | UpdateAddButton();
82 | BooleanSubmit = ReactiveCommand.Create(e =>
83 | {
84 | if (!e) return this;
85 | AuthorizedKeysFileLocal.PersistChangesInFile();
86 | ServerConnection.WriteAuthorizedKeysChangesToServer(AuthorizedKeysFileRemote);
87 | return this;
88 | });
89 | AddKey = ReactiveCommand.CreateFromTask(async e =>
90 | {
91 | await AuthorizedKeysFileRemote.AddAuthorizedKeyAsync(e);
92 | UpdateAddButton();
93 | return e;
94 | });
95 | }
96 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewModels/EditKnownHostsViewModel.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:45
6 |
7 | #endregion
8 |
9 | using System.Collections.ObjectModel;
10 | using System.Linq;
11 | using OpenSSH_GUI.Core.Enums;
12 | using OpenSSH_GUI.Core.Extensions;
13 | using OpenSSH_GUI.Core.Interfaces.KnownHosts;
14 | using OpenSSH_GUI.Core.Interfaces.Misc;
15 | using OpenSSH_GUI.Core.Lib.KnownHosts;
16 | using ReactiveUI;
17 |
18 | namespace OpenSSH_GUI.ViewModels;
19 |
20 | public class EditKnownHostsViewModel : ViewModelBase
21 | {
22 | private ObservableCollection _knownHostsLocal = [];
23 |
24 | private ObservableCollection _knownHostsRemote = [];
25 |
26 | public IServerConnection ServerConnection { get; private set; }
27 |
28 | private IKnownHostsFile KnownHostsFileLocal { get; set; }
29 | private IKnownHostsFile KnownHostsFileRemote { get; set; }
30 |
31 | public ObservableCollection KnownHostsRemote
32 | {
33 | get => _knownHostsRemote;
34 | private set => this.RaiseAndSetIfChanged(ref _knownHostsRemote, value);
35 | }
36 |
37 | public ObservableCollection KnownHostsLocal
38 | {
39 | get => _knownHostsLocal;
40 | private set => this.RaiseAndSetIfChanged(ref _knownHostsLocal, value);
41 | }
42 |
43 | public void SetServerConnection(ref IServerConnection connection)
44 | {
45 | ServerConnection = connection;
46 | KnownHostsFileLocal = new KnownHostsFile(SshConfigFiles.Known_Hosts.GetPathOfFile());
47 | KnownHostsFileRemote = ServerConnection.GetKnownHostsFromServer();
48 | KnownHostsLocal = new ObservableCollection(KnownHostsFileLocal.KnownHosts.OrderBy(e => e.Host));
49 | KnownHostsRemote = new ObservableCollection(KnownHostsFileRemote.KnownHosts.OrderBy(e => e.Host));
50 | BooleanSubmit = ReactiveCommand.CreateFromTask(async e =>
51 | {
52 | if (!e) return this;
53 | KnownHostsFileLocal.SyncKnownHosts(KnownHostsLocal);
54 | if (ServerConnection.IsConnected) KnownHostsFileRemote.SyncKnownHosts(KnownHostsRemote);
55 | await KnownHostsFileLocal.UpdateFile();
56 | if (ServerConnection.IsConnected) ServerConnection.WriteKnownHostsToServer(KnownHostsFileRemote);
57 | return this;
58 | });
59 | }
60 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewModels/EditSavedServerEntryViewModel.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:45
6 |
7 | #endregion
8 |
9 | using System.Collections.ObjectModel;
10 | using System.IO;
11 | using System.Linq;
12 | using OpenSSH_GUI.Core.Interfaces.Credentials;
13 | using OpenSSH_GUI.Core.Interfaces.Keys;
14 | using ReactiveUI;
15 |
16 | namespace OpenSSH_GUI.ViewModels;
17 |
18 | public sealed class EditSavedServerEntryViewModel : ViewModelBase
19 | {
20 | private string _password = "";
21 |
22 | private ISshKey _selectedKey;
23 |
24 | public EditSavedServerEntryViewModel()
25 | {
26 | BooleanSubmit =
27 | ReactiveCommand.Create(e =>
28 | {
29 | if (!e) return null;
30 | if (CredentialsToEdit is IPasswordConnectionCredentials pwcc) pwcc.Password = Password;
31 | if (CredentialsToEdit is IKeyConnectionCredentials kcc) kcc.Key = SelectedKey;
32 | return this;
33 | });
34 | }
35 |
36 | public IConnectionCredentials CredentialsToEdit { get; set; }
37 | public ObservableCollection Keys { get; set; }
38 |
39 | public ISshKey SelectedKey
40 | {
41 | get => _selectedKey;
42 | set => this.RaiseAndSetIfChanged(ref _selectedKey, value);
43 | }
44 |
45 | public string Password
46 | {
47 | get => _password;
48 | set => this.RaiseAndSetIfChanged(ref _password, value);
49 | }
50 |
51 | public bool IsPasswordKey => CredentialsToEdit is IPasswordConnectionCredentials;
52 |
53 | public void SetValues(ref ObservableCollection keys, IConnectionCredentials credentials)
54 | {
55 | Keys = keys;
56 | CredentialsToEdit = credentials;
57 | if (CredentialsToEdit is IPasswordConnectionCredentials pwcc) Password = pwcc.Password;
58 | SelectedKey = CredentialsToEdit is IKeyConnectionCredentials kcc
59 | ? Keys.FirstOrDefault(e => string.Equals(kcc.Key.Fingerprint, e.Fingerprint))
60 | : Keys.FirstOrDefault();
61 | }
62 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewModels/ExportWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:47
6 |
7 | #endregion
8 |
9 | using Avalonia;
10 | using Avalonia.Controls.ApplicationLifetimes;
11 | using ReactiveUI;
12 |
13 | namespace OpenSSH_GUI.ViewModels;
14 |
15 | public class ExportWindowViewModel : ViewModelBase
16 | {
17 | public ExportWindowViewModel()
18 | {
19 | BooleanSubmit = ReactiveCommand.Create(boolean =>
20 | {
21 | if (!boolean) return null;
22 | if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
23 | return null;
24 | var mainWindow = desktop.MainWindow;
25 | var clipboard = mainWindow.Clipboard;
26 |
27 | clipboard.SetTextAsync(Export).ConfigureAwait(false);
28 | return this;
29 | });
30 | }
31 |
32 | public string WindowTitle { get; set; } = "";
33 | public string Export { get; set; } = "";
34 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/ViewModels/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:47
6 |
7 | #endregion
8 |
9 | using Microsoft.Extensions.DependencyInjection;
10 | using Microsoft.Extensions.Logging;
11 | using Microsoft.Extensions.Logging.Abstractions;
12 | using ReactiveUI;
13 |
14 | namespace OpenSSH_GUI.ViewModels;
15 |
16 | public class ViewModelBase() : ViewModelBase(App.ServiceProvider.GetRequiredService>()) where T : class
17 | {
18 | public ReactiveCommand Submit { get; set; } = ReactiveCommand.Create(e => null);
19 | public ReactiveCommand BooleanSubmit { get; set; } = ReactiveCommand.Create(e => null);
20 | }
21 |
22 | public class ViewModelBase(ILogger? logger = null) : ReactiveObject
23 | {
24 | protected ILogger Logger => logger ?? NullLogger.Instance;
25 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/AddKeyWindow.axaml:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
27 |
28 |
29 |
30 |
32 |
34 |
35 |
36 |
37 |
38 |
40 |
42 |
43 |
47 |
48 |
50 |
52 |
53 |
54 |
55 |
56 |
58 |
60 |
61 |
62 |
63 |
65 |
66 |
67 |
69 |
70 |
82 |
83 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/AddKeyWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:37
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Resources.Wrapper;
10 | using OpenSSH_GUI.ViewModels;
11 | using ReactiveUI;
12 | using ReactiveUI.Validation.Extensions;
13 |
14 | namespace OpenSSH_GUI.Views;
15 |
16 | public partial class AddKeyWindow : WindowBase
17 | {
18 | public AddKeyWindow()
19 | {
20 | InitializeComponent();
21 | this.WhenActivated(d =>
22 | {
23 | this.BindValidation(ViewModel, model => model.KeyName,
24 | window => window.KeyFileNameValidation.Text);
25 | // d(ViewModel!.Submit.Subscribe(Close));
26 | });
27 | }
28 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/ApplicationSettingsWindow.axaml:
--------------------------------------------------------------------------------
1 |
15 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
42 |
43 |
50 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
84 |
85 |
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/ApplicationSettingsWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:37
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Resources.Wrapper;
10 | using OpenSSH_GUI.ViewModels;
11 | using ReactiveUI;
12 |
13 | namespace OpenSSH_GUI.Views;
14 |
15 | public partial class ApplicationSettingsWindow : WindowBase
16 | {
17 | public ApplicationSettingsWindow()
18 | {
19 | InitializeComponent();
20 | this.WhenActivated(d =>
21 | {
22 | d(ViewModel!.ShowEditEntry.RegisterHandler(async interaction =>
23 | {
24 | var dialog = new EditSavedServerEntry
25 | {
26 | DataContext = interaction.Input,
27 | Title = interaction.Input.CredentialsToEdit.Display
28 | };
29 | var result = await dialog.ShowDialog(this);
30 | interaction.SetOutput(result);
31 | }));
32 | });
33 | }
34 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/ConnectToServerWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:38
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Resources.Wrapper;
10 | using OpenSSH_GUI.ViewModels;
11 |
12 | namespace OpenSSH_GUI.Views;
13 |
14 | public partial class ConnectToServerWindow : WindowBase
15 | {
16 | public ConnectToServerWindow()
17 | {
18 | InitializeComponent();
19 | }
20 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/EditAuthorizedKeysWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:38
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Resources.Wrapper;
10 | using OpenSSH_GUI.ViewModels;
11 |
12 | namespace OpenSSH_GUI.Views;
13 |
14 | public partial class EditAuthorizedKeysWindow : WindowBase
15 | {
16 | public EditAuthorizedKeysWindow()
17 | {
18 | InitializeComponent();
19 | }
20 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/EditKnownHostsWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:38
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Resources.Wrapper;
10 | using OpenSSH_GUI.ViewModels;
11 |
12 | namespace OpenSSH_GUI.Views;
13 |
14 | public partial class EditKnownHostsWindow : WindowBase
15 | {
16 | public EditKnownHostsWindow()
17 | {
18 | InitializeComponent();
19 | }
20 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/EditSavedServerEntry.axaml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
19 |
20 |
23 |
24 |
27 |
34 |
36 |
43 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/EditSavedServerEntry.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:39
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Resources.Wrapper;
10 | using OpenSSH_GUI.ViewModels;
11 |
12 | namespace OpenSSH_GUI.Views;
13 |
14 | public partial class EditSavedServerEntry : WindowBase
15 | {
16 | public EditSavedServerEntry()
17 | {
18 | InitializeComponent();
19 | }
20 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/ExportWindow.axaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
28 |
39 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/ExportWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:39
6 |
7 | #endregion
8 |
9 | using OpenSSH_GUI.Resources.Wrapper;
10 | using OpenSSH_GUI.ViewModels;
11 |
12 | namespace OpenSSH_GUI.Views;
13 |
14 | public partial class ExportWindow : WindowBase
15 | {
16 | public ExportWindow()
17 | {
18 | InitializeComponent();
19 | }
20 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/Views/MainWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | #region CopyrightNotice
2 |
3 | // File Created by: Oliver Schantz
4 | // Created: 15.05.2024 - 00:05:44
5 | // Last edit: 15.05.2024 - 01:05:40
6 |
7 | #endregion
8 |
9 | using System.Threading.Tasks;
10 | using Avalonia.Controls;
11 | using Avalonia.ReactiveUI;
12 | using OpenSSH_GUI.ViewModels;
13 | using ReactiveUI;
14 |
15 | namespace OpenSSH_GUI.Views;
16 |
17 | public partial class MainWindow : ReactiveWindow
18 | {
19 | private const WindowStartupLocation DefaultWindowStartupLocation = WindowStartupLocation.CenterScreen;
20 |
21 | public MainWindow()
22 | {
23 | Icon = App.WindowIcon;
24 | InitializeComponent();
25 | this.WhenActivated(action => action(ViewModel!.ShowCreate.RegisterHandler(DoShowAddKeyAsync)));
26 | this.WhenActivated(action => action(ViewModel!.ShowEditKnownHosts.RegisterHandler(DoShowEditKnownHostsAsync)));
27 | this.WhenActivated(action => action(ViewModel!.ShowExportWindow.RegisterHandler(DoShowExportWindowAsync)));
28 | this.WhenActivated(action =>
29 | action(ViewModel!.ShowEditAuthorizedKeys.RegisterHandler(DoShowEditAuthorizedKeysWindowAsync)));
30 | this.WhenActivated(action =>
31 | action(ViewModel!.ShowConnectToServerWindow.RegisterHandler(DoShowConnectToServerWindowAsync)));
32 | this.WhenActivated(action =>
33 | action(ViewModel!.ShowAppSettings.RegisterHandler(DoShowApplicationSettingsWindowAsync)));
34 | }
35 |
36 | private async Task DoShowApplicationSettingsWindowAsync(
37 | IInteractionContext interaction)
38 | {
39 | var dialog = new ApplicationSettingsWindow
40 | {
41 | DataContext = interaction.Input,
42 | WindowStartupLocation = DefaultWindowStartupLocation
43 | };
44 | interaction.SetOutput(await dialog.ShowDialog(this));
45 | }
46 |
47 | private async Task DoShowConnectToServerWindowAsync(
48 | IInteractionContext interaction)
49 | {
50 | var dialog = new ConnectToServerWindow
51 | {
52 | DataContext = interaction.Input,
53 | WindowStartupLocation = DefaultWindowStartupLocation
54 | };
55 | interaction.SetOutput(await dialog.ShowDialog(this));
56 | }
57 |
58 | private async Task DoShowEditAuthorizedKeysWindowAsync(
59 | IInteractionContext interaction)
60 | {
61 | var dialog = new EditAuthorizedKeysWindow
62 | {
63 | DataContext = interaction.Input,
64 | WindowStartupLocation = DefaultWindowStartupLocation
65 | };
66 | interaction.SetOutput(await dialog.ShowDialog(this));
67 | }
68 |
69 | private async Task DoShowExportWindowAsync(
70 | IInteractionContext interaction)
71 | {
72 | var dialog = new ExportWindow
73 | {
74 | DataContext = interaction.Input,
75 | WindowStartupLocation = DefaultWindowStartupLocation
76 | };
77 | interaction.SetOutput(await dialog.ShowDialog(this));
78 | }
79 |
80 | private async Task DoShowAddKeyAsync(IInteractionContext interaction)
81 | {
82 | var dialog = new AddKeyWindow
83 | {
84 | DataContext = interaction.Input,
85 | WindowStartupLocation = DefaultWindowStartupLocation
86 | };
87 | interaction.SetOutput(await dialog.ShowDialog(this));
88 | }
89 |
90 | private async Task DoShowEditKnownHostsAsync(
91 | IInteractionContext interaction)
92 | {
93 | var dialog = new EditKnownHostsWindow
94 | {
95 | DataContext = interaction.Input,
96 | WindowStartupLocation = DefaultWindowStartupLocation
97 | };
98 | interaction.SetOutput(await dialog.ShowDialog(this));
99 | }
100 | }
--------------------------------------------------------------------------------
/OpenSSH_GUI/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/images/AddKeyWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/AddKeyWindow.png
--------------------------------------------------------------------------------
/images/AppSettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/AppSettings.png
--------------------------------------------------------------------------------
/images/ConnectToServerQuickConnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/ConnectToServerQuickConnect.png
--------------------------------------------------------------------------------
/images/ConnectToServerWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/ConnectToServerWindow.png
--------------------------------------------------------------------------------
/images/ConnectToServerWindowSuccess.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/ConnectToServerWindowSuccess.png
--------------------------------------------------------------------------------
/images/ConnectToServerWindowWithKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/ConnectToServerWindowWithKey.png
--------------------------------------------------------------------------------
/images/EditAuthorizedKeysWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/EditAuthorizedKeysWindow.png
--------------------------------------------------------------------------------
/images/EditAuthorizedKeysWindowRemote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/EditAuthorizedKeysWindowRemote.png
--------------------------------------------------------------------------------
/images/ExportKeyWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/ExportKeyWindow.png
--------------------------------------------------------------------------------
/images/FoundPasswordProtectedKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/FoundPasswordProtectedKey.png
--------------------------------------------------------------------------------
/images/KnownHostsWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/KnownHostsWindow.png
--------------------------------------------------------------------------------
/images/MainWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/MainWindow.png
--------------------------------------------------------------------------------
/images/NewMainUI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/NewMainUI.png
--------------------------------------------------------------------------------
/images/ProvidePasswordPrompt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/ProvidePasswordPrompt.png
--------------------------------------------------------------------------------
/images/SettingsContextMenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/SettingsContextMenu.png
--------------------------------------------------------------------------------
/images/ShowForgetPws.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/ShowForgetPws.png
--------------------------------------------------------------------------------
/images/Sorted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/Sorted.png
--------------------------------------------------------------------------------
/images/tooltip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/tooltip.png
--------------------------------------------------------------------------------
/images/tooltipKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/tooltipKey.png
--------------------------------------------------------------------------------
/images/tooltipServer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frequency403/OpenSSH-GUI/5455133b1a58a9ac0d7df7335265dbbfb789d744/images/tooltipServer.png
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/qodana.yaml:
--------------------------------------------------------------------------------
1 | version: "1.0"
2 | linter: jetbrains/qodana-dotnet:2024.1
3 | profile:
4 | name: qodana.recommended
5 | include:
6 | - name: CheckDependencyLicenses
--------------------------------------------------------------------------------