From 0c8da39425059ac3dd8b9f436147e36e3310828e Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 10:47:29 -0700 Subject: [PATCH 01/10] Add a setting to choose the cert location --- SyncKusto/App.config | 3 +++ SyncKusto/Properties/Settings.Designer.cs | 12 ++++++++++ SyncKusto/Properties/Settings.settings | 3 +++ SyncKusto/SettingsForm.Designer.cs | 28 +++++++++++++++++++++- SyncKusto/SettingsForm.cs | 5 ++++ SyncKusto/SettingsForm.resx | 6 ++--- SyncKusto/SettingsWrapper.cs | 29 ++++++++++++++++++++--- 7 files changed, 79 insertions(+), 7 deletions(-) diff --git a/SyncKusto/App.config b/SyncKusto/App.config index 7c7df63..54e72a4 100644 --- a/SyncKusto/App.config +++ b/SyncKusto/App.config @@ -34,6 +34,9 @@ True + + + diff --git a/SyncKusto/Properties/Settings.Designer.cs b/SyncKusto/Properties/Settings.Designer.cs index 1159cf9..a9737d9 100644 --- a/SyncKusto/Properties/Settings.Designer.cs +++ b/SyncKusto/Properties/Settings.Designer.cs @@ -118,5 +118,17 @@ public bool UseLegacyCslExtension { this["UseLegacyCslExtension"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string CertificateLocation { + get { + return ((string)(this["CertificateLocation"])); + } + set { + this["CertificateLocation"] = value; + } + } } } diff --git a/SyncKusto/Properties/Settings.settings b/SyncKusto/Properties/Settings.settings index 6240f1d..d0ce594 100644 --- a/SyncKusto/Properties/Settings.settings +++ b/SyncKusto/Properties/Settings.settings @@ -26,5 +26,8 @@ True + + + \ No newline at end of file diff --git a/SyncKusto/SettingsForm.Designer.cs b/SyncKusto/SettingsForm.Designer.cs index 6642e7a..b86dd99 100644 --- a/SyncKusto/SettingsForm.Designer.cs +++ b/SyncKusto/SettingsForm.Designer.cs @@ -52,6 +52,8 @@ private void InitializeComponent() this.cbTableFieldsOnNewLine = new System.Windows.Forms.CheckBox(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOk = new System.Windows.Forms.Button(); + this.label6 = new System.Windows.Forms.Label(); + this.cbCertLocation = new System.Windows.Forms.ComboBox(); this.tabControl.SuspendLayout(); this.tpTempDatabase.SuspendLayout(); this.tcEntraId.SuspendLayout(); @@ -155,6 +157,8 @@ private void InitializeComponent() // // tcEntraId // + this.tcEntraId.Controls.Add(this.cbCertLocation); + this.tcEntraId.Controls.Add(this.label6); this.tcEntraId.Controls.Add(this.txtAuthority); this.tcEntraId.Controls.Add(this.label3); this.tcEntraId.Location = new System.Drawing.Point(4, 29); @@ -162,7 +166,7 @@ private void InitializeComponent() this.tcEntraId.Padding = new System.Windows.Forms.Padding(3, 10, 3, 10); this.tcEntraId.Size = new System.Drawing.Size(526, 248); this.tcEntraId.TabIndex = 1; - this.tcEntraId.Text = "Entra Id"; + this.tcEntraId.Text = "Authentication"; this.tcEntraId.UseVisualStyleBackColor = true; // // txtAuthority @@ -299,6 +303,26 @@ private void InitializeComponent() this.btnOk.UseVisualStyleBackColor = true; this.btnOk.Click += new System.EventHandler(this.btnOk_Click); // + // label6 + // + this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label6.Location = new System.Drawing.Point(4, 143); + this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(498, 25); + this.label6.TabIndex = 110; + this.label6.Text = "Certificate Location for Subject Name Issuer Auth:"; + // + // cbCertLocation + // + this.cbCertLocation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbCertLocation.FormattingEnabled = true; + this.cbCertLocation.Location = new System.Drawing.Point(8, 171); + this.cbCertLocation.Name = "cbCertLocation"; + this.cbCertLocation.Size = new System.Drawing.Size(501, 28); + this.cbCertLocation.TabIndex = 111; + // // SettingsForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); @@ -347,5 +371,7 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox cbUseLegacyCslExtension; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnOk; + private System.Windows.Forms.ComboBox cbCertLocation; + private System.Windows.Forms.Label label6; } } \ No newline at end of file diff --git a/SyncKusto/SettingsForm.cs b/SyncKusto/SettingsForm.cs index ead4f3f..6287c03 100644 --- a/SyncKusto/SettingsForm.cs +++ b/SyncKusto/SettingsForm.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.Security.Cryptography.X509Certificates; using System.Windows.Forms; namespace SyncKusto @@ -23,6 +24,8 @@ public partial class SettingsForm : Form public SettingsForm() { InitializeComponent(); + + cbCertLocation.Items.AddRange(Enum.GetNames(typeof(StoreLocation))); } /// @@ -39,6 +42,7 @@ private void SettingsForm_Load(object sender, EventArgs e) cbTableFieldsOnNewLine.Checked = SettingsWrapper.TableFieldsOnNewLine ?? false; cbCreateMerge.Checked = SettingsWrapper.CreateMergeEnabled ?? false; cbUseLegacyCslExtension.Checked = SettingsWrapper.UseLegacyCslExtension ?? false; + cbCertLocation.SelectedItem = SettingsWrapper.CertificateLocation; } /// @@ -56,6 +60,7 @@ private void btnOk_Click(object sender, System.EventArgs e) SettingsWrapper.KustoObjectDropWarning = chkTableDropWarning.Checked; SettingsWrapper.AADAuthority = txtAuthority.Text; SettingsWrapper.UseLegacyCslExtension = cbUseLegacyCslExtension.Checked; + SettingsWrapper.CertificateLocation = cbCertLocation.SelectedItem.ToString(); // Only check the Kusto settings if they changed if (SettingsWrapper.KustoClusterForTempDatabases != txtKustoCluster.Text || diff --git a/SyncKusto/SettingsForm.resx b/SyncKusto/SettingsForm.resx index daef95a..d742468 100644 --- a/SyncKusto/SettingsForm.resx +++ b/SyncKusto/SettingsForm.resx @@ -117,12 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - When the local file system is selected as the source or the target, SyncKusto loads all of the files into a temporary database and then asks Kusto for the database schema. Specify a Kusto cluster and database to use for the comparison. You must be a database admin. - Specify the Entra ID authority to be used for authentication (e.g. contoso.com). Depending on your tenant configuration, this may be optional, unless you're connecting with an application id in which case it is required: + + When the local file system is selected as the source or the target, SyncKusto loads all of the files into a temporary database and then asks Kusto for the database schema. Specify a Kusto cluster and database to use for the comparison. You must be a database admin. + These settings are only applied when a file is written. They do not affect the comparison operation. To apply this to all existing files, delete all the files locally, execute the comparison again and then update the local files. diff --git a/SyncKusto/SettingsWrapper.cs b/SyncKusto/SettingsWrapper.cs index a3cf9ad..e298d0b 100644 --- a/SyncKusto/SettingsWrapper.cs +++ b/SyncKusto/SettingsWrapper.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using SyncKusto.Properties; +using System.Security.Cryptography.X509Certificates; namespace SyncKusto { @@ -50,7 +51,7 @@ public static string TemporaryKustoDatabase } /// - /// The AAD Authority to use to authenticate a user. For user auth, this might work when it's empty depending on the tenant configuration, + /// The AAD Authority to use to authenticate a user. For user auth, this might work when it's empty depending on the tenant configuration, /// but it's always required for AAD application auth. /// public static string AADAuthority @@ -71,7 +72,7 @@ public static bool KustoObjectDropWarning get { bool? currentSetting = Settings.Default["KustoObjectDropWarning"] as bool?; - return currentSetting??false; + return currentSetting ?? false; } set { @@ -123,8 +124,30 @@ public static bool? UseLegacyCslExtension /// Gets the file extension to use throughout the application when reading and writing Kusto files /// public static string FileExtension - { + { get => SettingsWrapper.UseLegacyCslExtension.GetValueOrDefault() ? "csl" : "kql"; } + + /// + /// The certificate location to search use when displaing certs in the Subject Name Issuer cert picker. + /// + public static string CertificateLocation + { + get + { + var currentValue = Settings.Default["CertificateLocation"] as string; + if (string.IsNullOrWhiteSpace(currentValue)) + { + return StoreLocation.CurrentUser.ToString(); + } + + return currentValue; + } + set + { + Settings.Default["CertificateLocation"] = value; + Settings.Default.Save(); + } + } } } \ No newline at end of file From f783cd8c33aa9900f790a662ffd81f3768badaa5 Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 11:02:29 -0700 Subject: [PATCH 02/10] use the selected cert location --- SyncKusto/App.config | 2 +- SyncKusto/Properties/Settings.Designer.cs | 2 +- SyncKusto/Properties/Settings.settings | 2 +- SyncKusto/SchemaPickerControl.cs | 2 +- SyncKusto/SettingsForm.cs | 4 ++-- SyncKusto/SettingsWrapper.cs | 15 +++++++++++---- SyncKusto/Utilities/CertificateStore.cs | 11 +++-------- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/SyncKusto/App.config b/SyncKusto/App.config index 54e72a4..9c9e5f5 100644 --- a/SyncKusto/App.config +++ b/SyncKusto/App.config @@ -35,7 +35,7 @@ True - + CurrentUser diff --git a/SyncKusto/Properties/Settings.Designer.cs b/SyncKusto/Properties/Settings.Designer.cs index a9737d9..217e07c 100644 --- a/SyncKusto/Properties/Settings.Designer.cs +++ b/SyncKusto/Properties/Settings.Designer.cs @@ -121,7 +121,7 @@ public bool UseLegacyCslExtension { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] + [global::System.Configuration.DefaultSettingValueAttribute("CurrentUser")] public string CertificateLocation { get { return ((string)(this["CertificateLocation"])); diff --git a/SyncKusto/Properties/Settings.settings b/SyncKusto/Properties/Settings.settings index d0ce594..cb785ab 100644 --- a/SyncKusto/Properties/Settings.settings +++ b/SyncKusto/Properties/Settings.settings @@ -27,7 +27,7 @@ True - + CurrentUser \ No newline at end of file diff --git a/SyncKusto/SchemaPickerControl.cs b/SyncKusto/SchemaPickerControl.cs index d8a7a01..103edf4 100644 --- a/SyncKusto/SchemaPickerControl.cs +++ b/SyncKusto/SchemaPickerControl.cs @@ -297,7 +297,7 @@ private void btnCertificate_Click(object sender, EventArgs e) { // Show the certificate selection dialog var selectedCertificateCollection = X509Certificate2UI.SelectFromCollection( - CertificateStore.GetAllCertificates(), + CertificateStore.GetAllCertificates(SettingsWrapper.CertificateLocation), "Select a certificate", "Choose a certificate for authentication", X509SelectionFlag.SingleSelection); diff --git a/SyncKusto/SettingsForm.cs b/SyncKusto/SettingsForm.cs index 6287c03..5332410 100644 --- a/SyncKusto/SettingsForm.cs +++ b/SyncKusto/SettingsForm.cs @@ -25,7 +25,7 @@ public SettingsForm() { InitializeComponent(); - cbCertLocation.Items.AddRange(Enum.GetNames(typeof(StoreLocation))); + cbCertLocation.DataSource = Enum.GetValues(typeof(StoreLocation)); } /// @@ -60,7 +60,7 @@ private void btnOk_Click(object sender, System.EventArgs e) SettingsWrapper.KustoObjectDropWarning = chkTableDropWarning.Checked; SettingsWrapper.AADAuthority = txtAuthority.Text; SettingsWrapper.UseLegacyCslExtension = cbUseLegacyCslExtension.Checked; - SettingsWrapper.CertificateLocation = cbCertLocation.SelectedItem.ToString(); + SettingsWrapper.CertificateLocation = (StoreLocation)cbCertLocation.SelectedItem; // Only check the Kusto settings if they changed if (SettingsWrapper.KustoClusterForTempDatabases != txtKustoCluster.Text || diff --git a/SyncKusto/SettingsWrapper.cs b/SyncKusto/SettingsWrapper.cs index e298d0b..edc0197 100644 --- a/SyncKusto/SettingsWrapper.cs +++ b/SyncKusto/SettingsWrapper.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using SyncKusto.Properties; +using System; using System.Security.Cryptography.X509Certificates; namespace SyncKusto @@ -131,21 +132,27 @@ public static string FileExtension /// /// The certificate location to search use when displaing certs in the Subject Name Issuer cert picker. /// - public static string CertificateLocation + public static StoreLocation CertificateLocation { get { var currentValue = Settings.Default["CertificateLocation"] as string; if (string.IsNullOrWhiteSpace(currentValue)) { - return StoreLocation.CurrentUser.ToString(); + return StoreLocation.CurrentUser; } - return currentValue; + if (Enum.TryParse(currentValue, out StoreLocation result)) + { + return result; + } + + // Couldn't parse so we'll go with CurrentUser. + return StoreLocation.CurrentUser; } set { - Settings.Default["CertificateLocation"] = value; + Settings.Default["CertificateLocation"] = value.ToString(); Settings.Default.Save(); } } diff --git a/SyncKusto/Utilities/CertificateStore.cs b/SyncKusto/Utilities/CertificateStore.cs index f364508..5860211 100644 --- a/SyncKusto/Utilities/CertificateStore.cs +++ b/SyncKusto/Utilities/CertificateStore.cs @@ -30,16 +30,11 @@ public static X509Certificate2 GetCertificate(string thumbprint) /// Get all the certificates in both the Current User and Local Machine locations. /// /// A collection of certificates - public static X509Certificate2Collection GetAllCertificates() + public static X509Certificate2Collection GetAllCertificates(StoreLocation storeLocation) { - X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + var store = new X509Store(StoreName.My, storeLocation); store.Open(OpenFlags.ReadOnly); - X509Certificate2Collection certificates = store.Certificates; - store.Close(); - - store = new X509Store(StoreName.My, StoreLocation.LocalMachine); - store.Open(OpenFlags.ReadOnly); - certificates.AddRange(store.Certificates); + var certificates = store.Certificates; store.Close(); return certificates; From 7c2985a642e596ee90e3ee4d6a15731332fce625 Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 11:03:34 -0700 Subject: [PATCH 03/10] throw exception when it can't be mapped --- SyncKusto/SettingsWrapper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SyncKusto/SettingsWrapper.cs b/SyncKusto/SettingsWrapper.cs index edc0197..0d8af97 100644 --- a/SyncKusto/SettingsWrapper.cs +++ b/SyncKusto/SettingsWrapper.cs @@ -147,8 +147,7 @@ public static StoreLocation CertificateLocation return result; } - // Couldn't parse so we'll go with CurrentUser. - return StoreLocation.CurrentUser; + throw new Exception($"Could not map {currentValue} to StoreLocation enum type."); } set { From f252d89f0629ed684b899686c04e37b0815afa01 Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 11:16:21 -0700 Subject: [PATCH 04/10] convert the cluster/db text boxes to be combo boxes --- SyncKusto/SchemaPickerControl.Designer.cs | 44 +++++++++++------------ SyncKusto/SchemaPickerControl.cs | 14 ++++---- SyncKusto/SettingsForm.Designer.cs | 44 +++++++++++------------ SyncKusto/SettingsForm.resx | 6 ++-- 4 files changed, 54 insertions(+), 54 deletions(-) diff --git a/SyncKusto/SchemaPickerControl.Designer.cs b/SyncKusto/SchemaPickerControl.Designer.cs index 9cf9254..19c851b 100644 --- a/SyncKusto/SchemaPickerControl.Designer.cs +++ b/SyncKusto/SchemaPickerControl.Designer.cs @@ -49,9 +49,7 @@ private void InitializeComponent() this.lblAppKey = new System.Windows.Forms.Label(); this.txtAppId = new System.Windows.Forms.TextBox(); this.lblAppId = new System.Windows.Forms.Label(); - this.txtDatabase = new System.Windows.Forms.TextBox(); this.lblDatabase = new System.Windows.Forms.Label(); - this.txtCluster = new System.Windows.Forms.TextBox(); this.lblCluster = new System.Windows.Forms.Label(); this.pnlFilePath = new System.Windows.Forms.Panel(); this.lblExample = new System.Windows.Forms.Label(); @@ -59,6 +57,8 @@ private void InitializeComponent() this.btnChooseDirectory = new System.Windows.Forms.Button(); this.rbKusto = new System.Windows.Forms.RadioButton(); this.rbFilePath = new System.Windows.Forms.RadioButton(); + this.cbCluster = new System.Windows.Forms.ComboBox(); + this.cbDatabase = new System.Windows.Forms.ComboBox(); this.grpSourceSchema.SuspendLayout(); this.pnlKusto.SuspendLayout(); this.grpAuthentication.SuspendLayout(); @@ -93,10 +93,10 @@ private void InitializeComponent() // // pnlKusto // + this.pnlKusto.Controls.Add(this.cbDatabase); + this.pnlKusto.Controls.Add(this.cbCluster); this.pnlKusto.Controls.Add(this.grpAuthentication); - this.pnlKusto.Controls.Add(this.txtDatabase); this.pnlKusto.Controls.Add(this.lblDatabase); - this.pnlKusto.Controls.Add(this.txtCluster); this.pnlKusto.Controls.Add(this.lblCluster); this.pnlKusto.Location = new System.Drawing.Point(9, 66); this.pnlKusto.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); @@ -238,14 +238,6 @@ private void InitializeComponent() this.lblAppId.TabIndex = 4; this.lblAppId.Text = "App Id:"; // - // txtDatabase - // - this.txtDatabase.Location = new System.Drawing.Point(99, 44); - this.txtDatabase.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.txtDatabase.Name = "txtDatabase"; - this.txtDatabase.Size = new System.Drawing.Size(314, 26); - this.txtDatabase.TabIndex = 3; - // // lblDatabase // this.lblDatabase.AutoSize = true; @@ -256,14 +248,6 @@ private void InitializeComponent() this.lblDatabase.TabIndex = 2; this.lblDatabase.Text = "Database:"; // - // txtCluster - // - this.txtCluster.Location = new System.Drawing.Point(99, 4); - this.txtCluster.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.txtCluster.Name = "txtCluster"; - this.txtCluster.Size = new System.Drawing.Size(314, 26); - this.txtCluster.TabIndex = 1; - // // lblCluster // this.lblCluster.AutoSize = true; @@ -342,6 +326,22 @@ private void InitializeComponent() this.rbFilePath.UseVisualStyleBackColor = true; this.rbFilePath.CheckedChanged += new System.EventHandler(this.rbFilePath_CheckedChanged); // + // cbCluster + // + this.cbCluster.FormattingEnabled = true; + this.cbCluster.Location = new System.Drawing.Point(99, 6); + this.cbCluster.Name = "cbCluster"; + this.cbCluster.Size = new System.Drawing.Size(314, 28); + this.cbCluster.TabIndex = 5; + // + // cbDatabase + // + this.cbDatabase.FormattingEnabled = true; + this.cbDatabase.Location = new System.Drawing.Point(99, 43); + this.cbDatabase.Name = "cbDatabase"; + this.cbDatabase.Size = new System.Drawing.Size(314, 28); + this.cbDatabase.TabIndex = 6; + // // SchemaPickerControl // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); @@ -374,9 +374,7 @@ private void InitializeComponent() private System.Windows.Forms.RadioButton rbKusto; private System.Windows.Forms.RadioButton rbFilePath; private System.Windows.Forms.Panel pnlKusto; - private System.Windows.Forms.TextBox txtDatabase; private System.Windows.Forms.Label lblDatabase; - private System.Windows.Forms.TextBox txtCluster; private System.Windows.Forms.Label lblCluster; private System.Windows.Forms.GroupBox grpAuthentication; private System.Windows.Forms.Panel pnlApplicationAuthentication; @@ -393,5 +391,7 @@ private void InitializeComponent() private System.Windows.Forms.Label lblCertificate; private System.Windows.Forms.TextBox txtAppIdSni; private System.Windows.Forms.Label lblAppIdSni; + private System.Windows.Forms.ComboBox cbCluster; + private System.Windows.Forms.ComboBox cbDatabase; } } diff --git a/SyncKusto/SchemaPickerControl.cs b/SyncKusto/SchemaPickerControl.cs index 103edf4..0570ddf 100644 --- a/SyncKusto/SchemaPickerControl.cs +++ b/SyncKusto/SchemaPickerControl.cs @@ -106,19 +106,19 @@ public KustoConnectionStringBuilder KustoConnection switch (Authentication) { case AuthenticationMode.AadFederated: - return QueryEngine.GetKustoConnectionStringBuilder(txtCluster.Text, txtDatabase.Text); + return QueryEngine.GetKustoConnectionStringBuilder(cbCluster.Text, cbDatabase.Text); case AuthenticationMode.AadApplication: return QueryEngine.GetKustoConnectionStringBuilder( - txtCluster.Text, - txtDatabase.Text, + cbCluster.Text, + cbDatabase.Text, aadClientId: txtAppId.Text, aadClientKey: txtAppKey.Text); case AuthenticationMode.AadApplicationSni: return QueryEngine.GetKustoConnectionStringBuilder( - txtCluster.Text, - txtDatabase.Text, + cbCluster.Text, + cbDatabase.Text, aadClientId: txtAppIdSni.Text, certificateThumbprint: txtCertificate.Text); @@ -160,8 +160,8 @@ private bool FilePathSourceSpecification() => private bool KustoSourceSpecification() => Spec .IsTrue(s => s.SourceSelection == SourceSelection.Kusto()) - .And(Spec.NonEmptyString(s => s.txtCluster.Text)) - .And(Spec.NonEmptyString(s => s.txtDatabase.Text)) + .And(Spec.NonEmptyString(s => s.cbCluster.Text)) + .And(Spec.NonEmptyString(s => s.cbDatabase.Text)) .And( Spec.IsTrue(s => s.Authentication == AuthenticationMode.AadFederated) .Or(Spec diff --git a/SyncKusto/SettingsForm.Designer.cs b/SyncKusto/SettingsForm.Designer.cs index b86dd99..106c4af 100644 --- a/SyncKusto/SettingsForm.Designer.cs +++ b/SyncKusto/SettingsForm.Designer.cs @@ -41,6 +41,8 @@ private void InitializeComponent() this.txtKustoCluster = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.tcEntraId = new System.Windows.Forms.TabPage(); + this.cbCertLocation = new System.Windows.Forms.ComboBox(); + this.label6 = new System.Windows.Forms.Label(); this.txtAuthority = new System.Windows.Forms.TextBox(); this.label3 = new System.Windows.Forms.Label(); this.tcWarnings = new System.Windows.Forms.TabPage(); @@ -52,8 +54,6 @@ private void InitializeComponent() this.cbTableFieldsOnNewLine = new System.Windows.Forms.CheckBox(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOk = new System.Windows.Forms.Button(); - this.label6 = new System.Windows.Forms.Label(); - this.cbCertLocation = new System.Windows.Forms.ComboBox(); this.tabControl.SuspendLayout(); this.tpTempDatabase.SuspendLayout(); this.tcEntraId.SuspendLayout(); @@ -169,6 +169,26 @@ private void InitializeComponent() this.tcEntraId.Text = "Authentication"; this.tcEntraId.UseVisualStyleBackColor = true; // + // cbCertLocation + // + this.cbCertLocation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbCertLocation.FormattingEnabled = true; + this.cbCertLocation.Location = new System.Drawing.Point(8, 191); + this.cbCertLocation.Name = "cbCertLocation"; + this.cbCertLocation.Size = new System.Drawing.Size(501, 28); + this.cbCertLocation.TabIndex = 111; + // + // label6 + // + this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label6.Location = new System.Drawing.Point(4, 163); + this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(498, 25); + this.label6.TabIndex = 110; + this.label6.Text = "Certificate Location for Subject Name Issuer Auth:"; + // // txtAuthority // this.txtAuthority.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) @@ -303,26 +323,6 @@ private void InitializeComponent() this.btnOk.UseVisualStyleBackColor = true; this.btnOk.Click += new System.EventHandler(this.btnOk_Click); // - // label6 - // - this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.label6.Location = new System.Drawing.Point(4, 143); - this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(498, 25); - this.label6.TabIndex = 110; - this.label6.Text = "Certificate Location for Subject Name Issuer Auth:"; - // - // cbCertLocation - // - this.cbCertLocation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cbCertLocation.FormattingEnabled = true; - this.cbCertLocation.Location = new System.Drawing.Point(8, 171); - this.cbCertLocation.Name = "cbCertLocation"; - this.cbCertLocation.Size = new System.Drawing.Size(501, 28); - this.cbCertLocation.TabIndex = 111; - // // SettingsForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); diff --git a/SyncKusto/SettingsForm.resx b/SyncKusto/SettingsForm.resx index d742468..daef95a 100644 --- a/SyncKusto/SettingsForm.resx +++ b/SyncKusto/SettingsForm.resx @@ -117,12 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Specify the Entra ID authority to be used for authentication (e.g. contoso.com). Depending on your tenant configuration, this may be optional, unless you're connecting with an application id in which case it is required: - When the local file system is selected as the source or the target, SyncKusto loads all of the files into a temporary database and then asks Kusto for the database schema. Specify a Kusto cluster and database to use for the comparison. You must be a database admin. + + Specify the Entra ID authority to be used for authentication (e.g. contoso.com). Depending on your tenant configuration, this may be optional, unless you're connecting with an application id in which case it is required: + These settings are only applied when a file is written. They do not affect the comparison operation. To apply this to all existing files, delete all the files locally, execute the comparison again and then update the local files. From 1d5f8d0305aa25af62f2847fb10e2674a6c36953 Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 14:24:29 -0700 Subject: [PATCH 05/10] Support saving and displaying recent clusters --- SyncKusto/Properties/Settings.Designer.cs | 22 +++++++++ SyncKusto/Properties/Settings.settings | 6 +++ SyncKusto/SchemaPickerControl.cs | 11 +++++ SyncKusto/SettingsWrapper.cs | 60 ++++++++++++++++++++++- 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/SyncKusto/Properties/Settings.Designer.cs b/SyncKusto/Properties/Settings.Designer.cs index 217e07c..21d0521 100644 --- a/SyncKusto/Properties/Settings.Designer.cs +++ b/SyncKusto/Properties/Settings.Designer.cs @@ -130,5 +130,27 @@ public string CertificateLocation { this["CertificateLocation"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::System.Collections.Specialized.StringCollection RecentClusters { + get { + return ((global::System.Collections.Specialized.StringCollection)(this["RecentClusters"])); + } + set { + this["RecentClusters"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::System.Collections.Specialized.StringCollection RecentDatabases { + get { + return ((global::System.Collections.Specialized.StringCollection)(this["RecentDatabases"])); + } + set { + this["RecentDatabases"] = value; + } + } } } diff --git a/SyncKusto/Properties/Settings.settings b/SyncKusto/Properties/Settings.settings index cb785ab..a13bd61 100644 --- a/SyncKusto/Properties/Settings.settings +++ b/SyncKusto/Properties/Settings.settings @@ -29,5 +29,11 @@ CurrentUser + + + + + + \ No newline at end of file diff --git a/SyncKusto/SchemaPickerControl.cs b/SyncKusto/SchemaPickerControl.cs index 0570ddf..6aefd4d 100644 --- a/SyncKusto/SchemaPickerControl.cs +++ b/SyncKusto/SchemaPickerControl.cs @@ -39,6 +39,8 @@ public SchemaPickerControl() ENTRA_ID_APP_SNI }); this.cmbAuthentication.SelectedIndex = 0; + + this.cbCluster.Items.AddRange(SettingsWrapper.RecentClusters.ToArray()); } /// @@ -308,5 +310,14 @@ private void btnCertificate_Click(object sender, EventArgs e) txtCertificate.Text = selectedCertificateCollection[0].Thumbprint; } } + + /// + /// For some of the inputs, we save the most recent values that were used. This method + /// updates the storage behind all those settings to include the most recently used values. + /// + public void SaveRecentValues() + { + SettingsWrapper.AddRecentCluster(cbCluster.Text); + } } } \ No newline at end of file diff --git a/SyncKusto/SettingsWrapper.cs b/SyncKusto/SettingsWrapper.cs index 0d8af97..9316afe 100644 --- a/SyncKusto/SettingsWrapper.cs +++ b/SyncKusto/SettingsWrapper.cs @@ -3,6 +3,10 @@ using SyncKusto.Properties; using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; using System.Security.Cryptography.X509Certificates; namespace SyncKusto @@ -130,7 +134,7 @@ public static string FileExtension } /// - /// The certificate location to search use when displaing certs in the Subject Name Issuer cert picker. + /// Get or set the certificate location to search use when displaing certs in the Subject Name Issuer cert picker. /// public static StoreLocation CertificateLocation { @@ -155,5 +159,59 @@ public static StoreLocation CertificateLocation Settings.Default.Save(); } } + + /// + /// Get or set the most recently used clusters + /// + public static List RecentClusters + { + get + { + var currentValue = Settings.Default["RecentClusters"] as StringCollection; + if (currentValue == null) + { + return new List(); + } + + return currentValue.Cast().ToList(); + } + set + { + if (!(value is IList)) + { + throw new ArgumentException("Value must be of type IList"); + } + + var sc = new StringCollection(); + sc.AddRange(value.ToArray()); + Settings.Default["RecentClusters"] = sc; + Settings.Default.Save(); + } + } + + public static void AddRecentCluster(string cluster) + { + if (string.IsNullOrWhiteSpace(cluster)) + { + return; + } + + var clusterList = RecentClusters; + + if (RecentClusters.Contains(cluster)) + { + // To bubble this to the top we'll first remove it from the list and then the next + // code block will add it back in. + clusterList.Remove(cluster); + } + + // Add it to the bottom of the list + clusterList.Insert(0, cluster); + while (clusterList.Count > 2) + { + clusterList.RemoveAt(clusterList.Count - 1); + } + RecentClusters = clusterList; + } } } \ No newline at end of file From 7ecccf805c5bd8bd025574a6169329b083db849e Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 14:30:21 -0700 Subject: [PATCH 06/10] Save recent databases too --- SyncKusto/SchemaPickerControl.cs | 1 + SyncKusto/SettingsWrapper.cs | 59 ++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/SyncKusto/SchemaPickerControl.cs b/SyncKusto/SchemaPickerControl.cs index 6aefd4d..8beb72e 100644 --- a/SyncKusto/SchemaPickerControl.cs +++ b/SyncKusto/SchemaPickerControl.cs @@ -318,6 +318,7 @@ private void btnCertificate_Click(object sender, EventArgs e) public void SaveRecentValues() { SettingsWrapper.AddRecentCluster(cbCluster.Text); + SettingsWrapper.AddRecentDatabase(cbDatabase.Text); } } } \ No newline at end of file diff --git a/SyncKusto/SettingsWrapper.cs b/SyncKusto/SettingsWrapper.cs index 9316afe..6a48a26 100644 --- a/SyncKusto/SettingsWrapper.cs +++ b/SyncKusto/SettingsWrapper.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Kusto.Data; using SyncKusto.Properties; using System; using System.Collections; @@ -189,29 +190,67 @@ public static List RecentClusters } } - public static void AddRecentCluster(string cluster) + /// + /// Get or set the most recently used clusters + /// + public static List RecentDatabases { - if (string.IsNullOrWhiteSpace(cluster)) + get { - return; + var currentValue = Settings.Default["RecentDatabases"] as StringCollection; + if (currentValue == null) + { + return new List(); + } + + return currentValue.Cast().ToList(); + } + set + { + if (!(value is IList)) + { + throw new ArgumentException("Value must be of type IList"); + } + + var sc = new StringCollection(); + sc.AddRange(value.ToArray()); + Settings.Default["RecentDatabases"] = sc; + Settings.Default.Save(); } + } - var clusterList = RecentClusters; + public static void AddRecentCluster(string cluster) + { + RecentClusters = AddRecentItem(RecentClusters, cluster); + } - if (RecentClusters.Contains(cluster)) + public static void AddRecentDatabase(string database) + { + RecentDatabases = AddRecentItem(RecentDatabases, database); + } + + private static List AddRecentItem(List itemList, string item) + { + if (string.IsNullOrWhiteSpace(item)) + { + return itemList; + } + + if (itemList.Contains(item)) { // To bubble this to the top we'll first remove it from the list and then the next // code block will add it back in. - clusterList.Remove(cluster); + itemList.Remove(item); } // Add it to the bottom of the list - clusterList.Insert(0, cluster); - while (clusterList.Count > 2) + itemList.Insert(0, item); + while (itemList.Count > 10) { - clusterList.RemoveAt(clusterList.Count - 1); + itemList.RemoveAt(itemList.Count - 1); } - RecentClusters = clusterList; + + return itemList; } } } \ No newline at end of file From 5a2d49db19cd47e27159a4b237df66aab27cfbc6 Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 15:31:48 -0700 Subject: [PATCH 07/10] also reload the recent list after changes --- SyncKusto/MainForm.Designer.cs | 2 +- SyncKusto/MainForm.cs | 10 ++++++++++ SyncKusto/SchemaPickerControl.cs | 16 ++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/SyncKusto/MainForm.Designer.cs b/SyncKusto/MainForm.Designer.cs index 7bf062d..a822540 100644 --- a/SyncKusto/MainForm.Designer.cs +++ b/SyncKusto/MainForm.Designer.cs @@ -150,7 +150,7 @@ private void InitializeComponent() this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(81, 13); this.label2.TabIndex = 6; - this.label2.Text = "Build 20240419"; + this.label2.Text = "Build 20240422"; // // spcTargetHolder // diff --git a/SyncKusto/MainForm.cs b/SyncKusto/MainForm.cs index 9758c63..ac40320 100644 --- a/SyncKusto/MainForm.cs +++ b/SyncKusto/MainForm.cs @@ -147,6 +147,16 @@ IDatabaseSchema TryGetSchema(Func> schem spcSource.ReportProgress(string.Empty); spcTarget.ReportProgress(string.Empty); + + // Save the cluster and databases used in recent history to populate the combo boxes for + // next time and then reload them both. (Note that combining save an reload into a + // single operation would mean that the source recent history list wouldn't contain + // whatever was just used in the target schema so we keep them as separate steps.) + spcSource.SaveRecentValues(); + spcTarget.SaveRecentValues(); + spcSource.ReloadRecentValues(); + spcTarget.ReloadRecentValues(); + // Enable the update button now that a comparison has been generated. btnUpdate.Enabled = true; } diff --git a/SyncKusto/SchemaPickerControl.cs b/SyncKusto/SchemaPickerControl.cs index 8beb72e..2e8f526 100644 --- a/SyncKusto/SchemaPickerControl.cs +++ b/SyncKusto/SchemaPickerControl.cs @@ -41,6 +41,7 @@ public SchemaPickerControl() this.cmbAuthentication.SelectedIndex = 0; this.cbCluster.Items.AddRange(SettingsWrapper.RecentClusters.ToArray()); + this.cbDatabase.Items.AddRange(SettingsWrapper.RecentDatabases.ToArray()); } /// @@ -317,8 +318,19 @@ private void btnCertificate_Click(object sender, EventArgs e) /// public void SaveRecentValues() { - SettingsWrapper.AddRecentCluster(cbCluster.Text); - SettingsWrapper.AddRecentDatabase(cbDatabase.Text); + SettingsWrapper.AddRecentCluster(this.cbCluster.Text); + SettingsWrapper.AddRecentDatabase(this.cbDatabase.Text); + } + + /// + /// For the inputs where we store recents, reload them all with the latest values. + /// + public void ReloadRecentValues() + { + this.cbCluster.Items.Clear(); + this.cbCluster.Items.AddRange(SettingsWrapper.RecentClusters.ToArray()); + this.cbDatabase.Items.Clear(); + this.cbDatabase.Items.AddRange(SettingsWrapper.RecentDatabases.ToArray()); } } } \ No newline at end of file From 0df44032631b1e36bfc50e080817d70a6ca7dbfa Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 15:34:11 -0700 Subject: [PATCH 08/10] Comments --- SyncKusto/SettingsWrapper.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/SyncKusto/SettingsWrapper.cs b/SyncKusto/SettingsWrapper.cs index 6a48a26..9bf69bf 100644 --- a/SyncKusto/SettingsWrapper.cs +++ b/SyncKusto/SettingsWrapper.cs @@ -219,16 +219,35 @@ public static List RecentDatabases } } + /// + /// Include a cluster in the recent history list. If it's already in the list, it will be + /// moved to the top of the list. + /// + /// The cluster to include public static void AddRecentCluster(string cluster) { RecentClusters = AddRecentItem(RecentClusters, cluster); } + /// + /// Include a database in the recent history list. If it's already in the list, it will be + /// moved to the top of the list. + /// + /// The database to include public static void AddRecentDatabase(string database) { RecentDatabases = AddRecentItem(RecentDatabases, database); } + /// + /// Make sure that an item is included in a list. If it's a new item, it gets added at the + /// top of the list. If it's an existing item, that item gets moved to the top of the list. + /// If the list is longer than 10 items, the least recently used items are truncated to get + /// back to 10. + /// + /// The list of items to update. + /// The item to include in the list. + /// The updated list of items. private static List AddRecentItem(List itemList, string item) { if (string.IsNullOrWhiteSpace(item)) From 041521c634efde3e5e06ed780e400f27799e2b09 Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 15:54:25 -0700 Subject: [PATCH 09/10] Recent app ids are saved too --- SyncKusto/SchemaPickerControl.Designer.cs | 46 +++++++++++------------ SyncKusto/SchemaPickerControl.cs | 14 +++++-- SyncKusto/SettingsWrapper.cs | 41 +++++++++++++++++++- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/SyncKusto/SchemaPickerControl.Designer.cs b/SyncKusto/SchemaPickerControl.Designer.cs index 19c851b..95c35a9 100644 --- a/SyncKusto/SchemaPickerControl.Designer.cs +++ b/SyncKusto/SchemaPickerControl.Designer.cs @@ -41,13 +41,11 @@ private void InitializeComponent() this.btnCertificate = new System.Windows.Forms.Button(); this.txtCertificate = new System.Windows.Forms.TextBox(); this.lblCertificate = new System.Windows.Forms.Label(); - this.txtAppIdSni = new System.Windows.Forms.TextBox(); this.lblAppIdSni = new System.Windows.Forms.Label(); this.cmbAuthentication = new System.Windows.Forms.ComboBox(); this.pnlApplicationAuthentication = new System.Windows.Forms.Panel(); this.txtAppKey = new System.Windows.Forms.TextBox(); this.lblAppKey = new System.Windows.Forms.Label(); - this.txtAppId = new System.Windows.Forms.TextBox(); this.lblAppId = new System.Windows.Forms.Label(); this.lblDatabase = new System.Windows.Forms.Label(); this.lblCluster = new System.Windows.Forms.Label(); @@ -59,6 +57,8 @@ private void InitializeComponent() this.rbFilePath = new System.Windows.Forms.RadioButton(); this.cbCluster = new System.Windows.Forms.ComboBox(); this.cbDatabase = new System.Windows.Forms.ComboBox(); + this.cbAppIdSni = new System.Windows.Forms.ComboBox(); + this.cbAppId = new System.Windows.Forms.ComboBox(); this.grpSourceSchema.SuspendLayout(); this.pnlKusto.SuspendLayout(); this.grpAuthentication.SuspendLayout(); @@ -106,8 +106,8 @@ private void InitializeComponent() // // grpAuthentication // - this.grpAuthentication.Controls.Add(this.pnlApplicationSniAuthentication); this.grpAuthentication.Controls.Add(this.cmbAuthentication); + this.grpAuthentication.Controls.Add(this.pnlApplicationSniAuthentication); this.grpAuthentication.Controls.Add(this.pnlApplicationAuthentication); this.grpAuthentication.Location = new System.Drawing.Point(10, 86); this.grpAuthentication.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); @@ -121,10 +121,10 @@ private void InitializeComponent() // // pnlApplicationSniAuthentication // + this.pnlApplicationSniAuthentication.Controls.Add(this.cbAppIdSni); this.pnlApplicationSniAuthentication.Controls.Add(this.btnCertificate); this.pnlApplicationSniAuthentication.Controls.Add(this.txtCertificate); this.pnlApplicationSniAuthentication.Controls.Add(this.lblCertificate); - this.pnlApplicationSniAuthentication.Controls.Add(this.txtAppIdSni); this.pnlApplicationSniAuthentication.Controls.Add(this.lblAppIdSni); this.pnlApplicationSniAuthentication.Location = new System.Drawing.Point(9, 71); this.pnlApplicationSniAuthentication.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); @@ -161,14 +161,6 @@ private void InitializeComponent() this.lblCertificate.TabIndex = 6; this.lblCertificate.Text = "Certificate Thumbprint:"; // - // txtAppIdSni - // - this.txtAppIdSni.Location = new System.Drawing.Point(180, 8); - this.txtAppIdSni.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.txtAppIdSni.Name = "txtAppIdSni"; - this.txtAppIdSni.Size = new System.Drawing.Size(204, 26); - this.txtAppIdSni.TabIndex = 5; - // // lblAppIdSni // this.lblAppIdSni.AutoSize = true; @@ -190,9 +182,9 @@ private void InitializeComponent() // // pnlApplicationAuthentication // + this.pnlApplicationAuthentication.Controls.Add(this.cbAppId); this.pnlApplicationAuthentication.Controls.Add(this.txtAppKey); this.pnlApplicationAuthentication.Controls.Add(this.lblAppKey); - this.pnlApplicationAuthentication.Controls.Add(this.txtAppId); this.pnlApplicationAuthentication.Controls.Add(this.lblAppId); this.pnlApplicationAuthentication.Location = new System.Drawing.Point(9, 71); this.pnlApplicationAuthentication.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); @@ -220,14 +212,6 @@ private void InitializeComponent() this.lblAppKey.TabIndex = 6; this.lblAppKey.Text = "App Key:"; // - // txtAppId - // - this.txtAppId.Location = new System.Drawing.Point(96, 8); - this.txtAppId.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.txtAppId.Name = "txtAppId"; - this.txtAppId.Size = new System.Drawing.Size(288, 26); - this.txtAppId.TabIndex = 5; - // // lblAppId // this.lblAppId.AutoSize = true; @@ -342,6 +326,22 @@ private void InitializeComponent() this.cbDatabase.Size = new System.Drawing.Size(314, 28); this.cbDatabase.TabIndex = 6; // + // cbAppIdSni + // + this.cbAppIdSni.FormattingEnabled = true; + this.cbAppIdSni.Location = new System.Drawing.Point(70, 8); + this.cbAppIdSni.Name = "cbAppIdSni"; + this.cbAppIdSni.Size = new System.Drawing.Size(314, 28); + this.cbAppIdSni.TabIndex = 9; + // + // cbAppId + // + this.cbAppId.FormattingEnabled = true; + this.cbAppId.Location = new System.Drawing.Point(70, 8); + this.cbAppId.Name = "cbAppId"; + this.cbAppId.Size = new System.Drawing.Size(314, 28); + this.cbAppId.TabIndex = 10; + // // SchemaPickerControl // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); @@ -380,7 +380,6 @@ private void InitializeComponent() private System.Windows.Forms.Panel pnlApplicationAuthentication; private System.Windows.Forms.TextBox txtAppKey; private System.Windows.Forms.Label lblAppKey; - private System.Windows.Forms.TextBox txtAppId; private System.Windows.Forms.Label lblAppId; private System.Windows.Forms.Label lblExample; private System.Windows.Forms.Label txtOperationProgress; @@ -389,9 +388,10 @@ private void InitializeComponent() private System.Windows.Forms.Button btnCertificate; private System.Windows.Forms.TextBox txtCertificate; private System.Windows.Forms.Label lblCertificate; - private System.Windows.Forms.TextBox txtAppIdSni; private System.Windows.Forms.Label lblAppIdSni; private System.Windows.Forms.ComboBox cbCluster; private System.Windows.Forms.ComboBox cbDatabase; + private System.Windows.Forms.ComboBox cbAppIdSni; + private System.Windows.Forms.ComboBox cbAppId; } } diff --git a/SyncKusto/SchemaPickerControl.cs b/SyncKusto/SchemaPickerControl.cs index 2e8f526..8d2d4de 100644 --- a/SyncKusto/SchemaPickerControl.cs +++ b/SyncKusto/SchemaPickerControl.cs @@ -115,14 +115,14 @@ public KustoConnectionStringBuilder KustoConnection return QueryEngine.GetKustoConnectionStringBuilder( cbCluster.Text, cbDatabase.Text, - aadClientId: txtAppId.Text, + aadClientId: cbAppId.Text, aadClientKey: txtAppKey.Text); case AuthenticationMode.AadApplicationSni: return QueryEngine.GetKustoConnectionStringBuilder( cbCluster.Text, cbDatabase.Text, - aadClientId: txtAppIdSni.Text, + aadClientId: cbAppIdSni.Text, certificateThumbprint: txtCertificate.Text); default: @@ -169,11 +169,11 @@ private bool KustoSourceSpecification() => Spec.IsTrue(s => s.Authentication == AuthenticationMode.AadFederated) .Or(Spec .IsTrue(s => s.Authentication == AuthenticationMode.AadApplication) - .And(Spec.NonEmptyString(s => s.txtAppId.Text) + .And(Spec.NonEmptyString(s => s.cbAppId.Text) .And(Spec.NonEmptyString(s => s.txtAppKey.Text)))) .Or(Spec .IsTrue(s => s.Authentication == AuthenticationMode.AadApplicationSni) - .And(Spec.NonEmptyString(s => s.txtAppIdSni.Text) + .And(Spec.NonEmptyString(s => s.cbAppIdSni.Text) .And(Spec.NonEmptyString(s => s.txtCertificate.Text))))) .IsSatisfiedBy(this); @@ -320,6 +320,8 @@ public void SaveRecentValues() { SettingsWrapper.AddRecentCluster(this.cbCluster.Text); SettingsWrapper.AddRecentDatabase(this.cbDatabase.Text); + SettingsWrapper.AddRecentAppId(this.cbAppId.Text); + SettingsWrapper.AddRecentAppId(this.cbAppIdSni.Text); } /// @@ -331,6 +333,10 @@ public void ReloadRecentValues() this.cbCluster.Items.AddRange(SettingsWrapper.RecentClusters.ToArray()); this.cbDatabase.Items.Clear(); this.cbDatabase.Items.AddRange(SettingsWrapper.RecentDatabases.ToArray()); + this.cbAppId.Items.Clear(); + this.cbAppId.Items.AddRange(SettingsWrapper.RecentAppIds.ToArray()); + this.cbAppIdSni.Items.Clear(); + this.cbAppIdSni.Items.AddRange(SettingsWrapper.RecentAppIds.ToArray()); } } } \ No newline at end of file diff --git a/SyncKusto/SettingsWrapper.cs b/SyncKusto/SettingsWrapper.cs index 9bf69bf..56bab5e 100644 --- a/SyncKusto/SettingsWrapper.cs +++ b/SyncKusto/SettingsWrapper.cs @@ -191,7 +191,7 @@ public static List RecentClusters } /// - /// Get or set the most recently used clusters + /// Get or set the most recently used databases /// public static List RecentDatabases { @@ -219,6 +219,35 @@ public static List RecentDatabases } } + /// + /// Get or set the most recently used application ids + /// + public static List RecentAppIds + { + get + { + var currentValue = Settings.Default["RecentAppIds"] as StringCollection; + if (currentValue == null) + { + return new List(); + } + + return currentValue.Cast().ToList(); + } + set + { + if (!(value is IList)) + { + throw new ArgumentException("Value must be of type IList"); + } + + var sc = new StringCollection(); + sc.AddRange(value.ToArray()); + Settings.Default["RecentAppIds"] = sc; + Settings.Default.Save(); + } + } + /// /// Include a cluster in the recent history list. If it's already in the list, it will be /// moved to the top of the list. @@ -239,6 +268,16 @@ public static void AddRecentDatabase(string database) RecentDatabases = AddRecentItem(RecentDatabases, database); } + /// + /// Include an application id in the recent history list. If it's already in the list, it + /// will be moved to the top of the list. + /// + /// The application id to include + public static void AddRecentAppId(string applicationId) + { + RecentAppIds = AddRecentItem(RecentAppIds, applicationId); + } + /// /// Make sure that an item is included in a list. If it's a new item, it gets added at the /// top of the list. If it's an existing item, that item gets moved to the top of the list. From f77315cc64089ec905709482b7079dc8fffb5920 Mon Sep 17 00:00:00 2001 From: Ben Martens Date: Mon, 22 Apr 2024 15:56:12 -0700 Subject: [PATCH 10/10] Include the setting for recent app ids --- SyncKusto/Properties/Settings.Designer.cs | 11 +++++++++++ SyncKusto/Properties/Settings.settings | 3 +++ 2 files changed, 14 insertions(+) diff --git a/SyncKusto/Properties/Settings.Designer.cs b/SyncKusto/Properties/Settings.Designer.cs index 21d0521..4831359 100644 --- a/SyncKusto/Properties/Settings.Designer.cs +++ b/SyncKusto/Properties/Settings.Designer.cs @@ -152,5 +152,16 @@ public string CertificateLocation { this["RecentDatabases"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::System.Collections.Specialized.StringCollection RecentAppIds { + get { + return ((global::System.Collections.Specialized.StringCollection)(this["RecentAppIds"])); + } + set { + this["RecentAppIds"] = value; + } + } } } diff --git a/SyncKusto/Properties/Settings.settings b/SyncKusto/Properties/Settings.settings index a13bd61..5a56b33 100644 --- a/SyncKusto/Properties/Settings.settings +++ b/SyncKusto/Properties/Settings.settings @@ -35,5 +35,8 @@ + + + \ No newline at end of file