diff --git a/DesktopEdge/App.xaml b/DesktopEdge/App.xaml index bcd9158d..8d816d24 100644 --- a/DesktopEdge/App.xaml +++ b/DesktopEdge/App.xaml @@ -1,7 +1,8 @@ - @@ -18,7 +19,7 @@ - + @@ -33,7 +34,7 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DesktopEdge/Assets/Images/disconnected.png b/DesktopEdge/Assets/Images/disconnected.png new file mode 100644 index 00000000..5853d7a6 Binary files /dev/null and b/DesktopEdge/Assets/Images/disconnected.png differ diff --git a/DesktopEdge/Assets/Images/name.png b/DesktopEdge/Assets/Images/name.png new file mode 100644 index 00000000..0897340f Binary files /dev/null and b/DesktopEdge/Assets/Images/name.png differ diff --git a/DesktopEdge/Assets/Images/statusconnected.png b/DesktopEdge/Assets/Images/statusconnected.png new file mode 100644 index 00000000..d558909d Binary files /dev/null and b/DesktopEdge/Assets/Images/statusconnected.png differ diff --git a/DesktopEdge/MainWindow.xaml b/DesktopEdge/MainWindow.xaml index 9b10b302..c6fd3c04 100644 --- a/DesktopEdge/MainWindow.xaml +++ b/DesktopEdge/MainWindow.xaml @@ -202,7 +202,7 @@ - + @@ -217,7 +217,7 @@ - + @@ -247,7 +247,8 @@ - + + diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs index c67c738d..14620335 100644 --- a/DesktopEdge/MainWindow.xaml.cs +++ b/DesktopEdge/MainWindow.xaml.cs @@ -156,6 +156,11 @@ namespace ZitiDesktopEdge { this.ShowMFA(identity, 1); } + /// + /// Show MFA for the identity and set the type of screen to show + /// + /// The Identity that is currently active + /// The type of screen to show - 1 Setup, 2 Authenticate, 3 Remove MFA, 4 Regenerate Codes private void ShowMFA(ZitiIdentity identity, int type) { MFASetup.Opacity = 0; MFASetup.Visibility = Visibility.Visible; @@ -342,10 +347,18 @@ namespace ZitiDesktopEdge { this.IdentityMenu.MainWindow = this; SetNotifyIcon("white"); + this.PreviewKeyDown += KeyPressed; MFASetup.OnLoad += MFASetup_OnLoad; IdentityMenu.OnMessage += IdentityMenu_OnMessage; } + private void KeyPressed(object sender, KeyEventArgs e) { + if (e.Key == Key.Escape) { + if (IdentityMenu.Visibility == Visibility.Visible) IdentityMenu.Visibility = Visibility.Collapsed; + else if (MainMenu.Visibility == Visibility.Visible) MainMenu.Visibility = Visibility.Collapsed; + } + } + private void MFASetup_OnLoad(bool isComplete, string title, string message) { if (isComplete) HideLoad(); else ShowLoad(title, message); @@ -503,6 +516,7 @@ namespace ZitiDesktopEdge { Application.Current.Properties.Add("Identities", new List()); MainMenu.OnAttachmentChange += AttachmentChanged; MainMenu.OnLogLevelChanged += LogLevelChanged; + MainMenu.OnShowBlurb += MainMenu_OnShowBlurb; IdentityMenu.OnError += IdentityMenu_OnError; try { @@ -524,7 +538,11 @@ namespace ZitiDesktopEdge { Placement(); } - private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) { + private void MainMenu_OnShowBlurb(string message) { + _ = ShowBlurbAsync(message, "", "info"); + } + + private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) { var found = identities.Find(id => id.Fingerprint == e.Fingerprint); if (found == null) { logger.Warn($"{e.Action} service event for {e.Fingerprint} but the provided identity fingerprint was not found!"); @@ -1287,7 +1305,14 @@ namespace ZitiDesktopEdge { /// /// The message to show /// The url or action name to execute - public async Task ShowBlurbAsync(string message, string url) { + public async Task ShowBlurbAsync(string message, string url, string level="error") { + RedBlurb.Visibility = Visibility.Collapsed; + InfoBlurb.Visibility = Visibility.Collapsed; + if (level=="error") { + RedBlurb.Visibility = Visibility.Visible; + } else { + InfoBlurb.Visibility = Visibility.Visible; + } Blurb.Content = message; _blurbUrl = url; BlurbArea.Visibility = Visibility.Visible; @@ -1377,6 +1402,11 @@ namespace ZitiDesktopEdge { someCommand = value; } } + + private void DoLoading(bool isComplete) { + if (isComplete) HideLoad(); + else ShowLoad("Loading", "Please Wait."); + } } public class ActionCommand : ICommand { diff --git a/DesktopEdge/Models/FilterData.cs b/DesktopEdge/Models/FilterData.cs new file mode 100644 index 00000000..408000e9 --- /dev/null +++ b/DesktopEdge/Models/FilterData.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZitiDesktopEdge.Models { + public class FilterData { + + public FilterData():this("", "", "asc") { } + + public FilterData(string searchFor, string sortBy, string sortHow) { + SearchFor = searchFor; + SortBy = sortBy; + SortHow = sortHow; + } + + public string SearchFor { get; set; } + public string SortBy { get; set; } + public string SortHow { get; set; } + } +} diff --git a/DesktopEdge/Models/ZitiService.cs b/DesktopEdge/Models/ZitiService.cs index c585b7b5..aa762fc5 100644 --- a/DesktopEdge/Models/ZitiService.cs +++ b/DesktopEdge/Models/ZitiService.cs @@ -1,133 +1,241 @@ using NLog; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ZitiDesktopEdge.DataStructures; - -namespace ZitiDesktopEdge.Models { +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +using ZitiDesktopEdge.DataStructures; + +namespace ZitiDesktopEdge.Models { public class ZitiService { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public string Name { get; set; } - public string[] Protocols { get; set; } - public Address[] Addresses { get; set; } - public PortRange[] Ports { get; set; } - public PostureCheck[] PostureChecks { get; set; } - public bool OwnsIntercept { get; set; } - public string AssignedIP { get; set; } - - private bool failingPostureCheck; - public bool HasFailingPostureCheck() { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public string Name { get; set; } + public string[] Protocols { get; set; } + public Address[] Addresses { get; set; } + public PortRange[] Ports { get; set; } + public PostureCheck[] PostureChecks { get; set; } + public bool OwnsIntercept { get; set; } + public string AssignedIP { get; set; } + + private bool failingPostureCheck; + public bool HasFailingPostureCheck() { return failingPostureCheck; - } - public bool IsAccessable { get; set; } - - public string Warning { - get { - if (this.OwnsIntercept) { - return ""; - } else { - return "this won't trigger right now"; //$"Another identity already mapped the specified hostname: {Host}.\nThis service is only available via IP"; - } - } - } - - public ZitiService() { - } - - public ZitiService(Service svc) { - this.Name = svc.Name; - this.AssignedIP = svc.AssignedIP; - this.Addresses = svc.Addresses; - this.Protocols = svc.Protocols == null ? null : svc.Protocols.Select(p => p.ToUpper()).ToArray(); - this.Ports = svc.Ports; - this.PostureChecks = svc.PostureChecks; + } + public bool IsAccessable { get; set; } + + public string Warning { + get { + if (this.OwnsIntercept) { + return ""; + } else { + return "this won't trigger right now"; //$"Another identity already mapped the specified hostname: {Host}.\nThis service is only available via IP"; + } + } + } + + public ZitiService() { + } + + public ZitiService(Service svc) { + this.Name = svc.Name; + this.AssignedIP = svc.AssignedIP; + this.Addresses = svc.Addresses; + this.Protocols = svc.Protocols == null ? null : svc.Protocols.Select(p => p.ToUpper()).ToArray(); + this.Ports = svc.Ports; + this.PostureChecks = svc.PostureChecks; this.OwnsIntercept = svc.OwnsIntercept; if (this.PostureChecks != null) { this.failingPostureCheck = this.PostureChecks.Any(p => !p.IsPassing); - } - this.IsAccessable = svc.IsAccessable; - //commented out for now logger.Warn("SERVICE: " + this.Name + " HAS FAILING POSTURE CHECK: " + failingPostureCheck); - } - - - - private ServiceMatrix builtMatrix = null; - public ServiceMatrix Matrix { - get { - if (builtMatrix == null) { - builtMatrix = new ServiceMatrix(); - List matrix = new List(this.Protocols.Length * this.Addresses.Length * this.Ports.Length); - - foreach (var proto in this.Protocols) { - foreach (var addy in this.Addresses) { - foreach (var port in this.Ports) { - ServiceMatrixElement m = new ServiceMatrixElement(); - m.Ports = port.ToString(); - m.Proto = proto.ToUpper(); - m.Address = addy.Hostname; - - matrix.Add(m); - } - } - } - builtMatrix.Elements = matrix; - } - - return builtMatrix; - } - } - - public override string ToString() { - string protos = ""; - if (Protocols?.Length > 0) { - if (Protocols.Length > 1) { - protos = "[" + string.Join(",", Protocols.Select(p => p.ToString())) + "]"; - } else { - protos = Protocols[0]; - } - } - string addys = ""; - if (Addresses?.Length > 0) { - if (Addresses.Length > 1) { - addys = "[" + string.Join(",", Addresses.Select(a => a.ToString()).OrderBy(o => o)) + "]"; - } else { - addys = Addresses[0].ToString(); - } - } - string ranges = ""; - if (Ports?.Length > 0) { - if (Ports.Length > 1) { - ranges = "[" + string.Join(",", Ports.Select(a => a.ToString()).OrderBy(o => o)) + "]"; - } else { - ranges = Ports[0].ToString(); - } - } - - return protos + ":" + addys + ":" + ranges; - } - } - - public class ServiceMatrix { - public List Elements { get; internal set; } - } - - public class ServiceMatrixElement { - public ServiceMatrixElement() { - Proto = ""; - Address = ""; - Ports = ""; - } - - public string Proto { get; set; } - public string Address { get; set; } - public string Ports { get; set; } - - public override string ToString() { - return Proto + " " + Address + " " + Ports; - } - } -} + } + this.IsAccessable = svc.IsAccessable; + //commented out for now logger.Warn("SERVICE: " + this.Name + " HAS FAILING POSTURE CHECK: " + failingPostureCheck); + } + + public string WarningMessage { + get { + string message = Warning; + if (this.PostureChecks != null && this.PostureChecks.Length > 0) { + List messages = new List(); + for (int i = 0; i < this.PostureChecks.Length; i++) { + if (!this.PostureChecks[i].IsPassing) { + messages = AppendMessage(messages, this.PostureChecks[i].QueryType); + } + } + if (messages.Count > 0) { + string checks = ""; + for (int i = 0; i < messages.Count; i++) { + checks += ((i > 0) ? ", " : "") + messages[i].Total + " " + messages[i].Message; + } + message = message + " Posture Check Failing: " + checks; + return message.Trim(); + } else return message; + } else return message; + } + } + private List AppendMessage(List items, string message) { + bool found = false; + for (int i = 0; i < items.Count; i++) { + if (items[i].Message == message) { + items[i].Total++; + found = true; + } + } + if (!found) { + MessageCount count = new MessageCount(); + count.Total = 1; + count.Message = message; + items.Add(count); + } + return items; + } + + public int WarnWidth { + get { + return (HasWarning) ? 30 : 0; + } + set { } + } + + public Visibility WarningVisibility { + get { + return (HasWarning) ? Visibility.Visible : Visibility.Collapsed; + } + set { } + } + + public bool HasWarning { + get { + if (!this.OwnsIntercept) return true; + else { + if (this.IsAccessable) { + return false; + } else { + if (this.PostureChecks==null) { + return false; + } else { + for (int i = 0; i < this.PostureChecks.Length; i++) { + if (!this.PostureChecks[i].IsPassing) { + return true; + } + } + return false; + } + } + } + } + set { } + } + + public string ProtocolString { + get { + string toReturn = ""; + for (int i = 0; i < this.Protocols.Length; i++) { + toReturn += ((i > 0) ? "," : "") + this.Protocols[i]; + } + return toReturn; + } + set { } + } + + public string PortString { + get { + string toReturn = ""; + for (int i = 0; i < this.Ports.Length; i++) { + toReturn += ((i > 0) ? "," : "") + this.Ports[i].ToString(); + } + return toReturn; + } + set { } + } + + public string AddressString { + get { + string toReturn = ""; + for (int i = 0; i < this.Addresses.Length; i++) { + toReturn += ((i > 0) ? "," : "") + this.Addresses[i].ToString(); + } + return toReturn; + } + set { } + } + + private ServiceMatrix builtMatrix = null; + public ServiceMatrix Matrix { + get { + if (builtMatrix == null) { + builtMatrix = new ServiceMatrix(); + List matrix = new List(this.Protocols.Length * this.Addresses.Length * this.Ports.Length); + + foreach (var proto in this.Protocols) { + foreach (var addy in this.Addresses) { + foreach (var port in this.Ports) { + ServiceMatrixElement m = new ServiceMatrixElement(); + m.Ports = port.ToString(); + m.Proto = proto.ToUpper(); + m.Address = addy.Hostname; + + matrix.Add(m); + } + } + } + builtMatrix.Elements = matrix; + } + + return builtMatrix; + } + } + + public override string ToString() { + string protos = ""; + if (Protocols?.Length > 0) { + if (Protocols.Length > 1) { + protos = "[" + string.Join(",", Protocols.Select(p => p.ToString())) + "]"; + } else { + protos = Protocols[0]; + } + } + string addys = ""; + if (Addresses?.Length > 0) { + if (Addresses.Length > 1) { + addys = "[" + string.Join(",", Addresses.Select(a => a.ToString()).OrderBy(o => o)) + "]"; + } else { + addys = Addresses[0].ToString(); + } + } + string ranges = ""; + if (Ports?.Length > 0) { + if (Ports.Length > 1) { + ranges = "[" + string.Join(",", Ports.Select(a => a.ToString()).OrderBy(o => o)) + "]"; + } else { + ranges = Ports[0].ToString(); + } + } + + return protos + ":" + addys + ":" + ranges; + } + } + + public class ServiceMatrix { + public List Elements { get; internal set; } + } + + public class ServiceMatrixElement { + public ServiceMatrixElement() { + Proto = ""; + Address = ""; + Ports = ""; + } + + public string Proto { get; set; } + public string Address { get; set; } + public string Ports { get; set; } + + public override string ToString() { + return Proto + " " + Address + " " + Ports; + } + } +} diff --git a/DesktopEdge/Properties/AssemblyInfo.cs b/DesktopEdge/Properties/AssemblyInfo.cs index ec3cefc3..127f5b62 100644 --- a/DesktopEdge/Properties/AssemblyInfo.cs +++ b/DesktopEdge/Properties/AssemblyInfo.cs @@ -50,6 +50,6 @@ using System.Windows; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.3.0")] -[assembly: AssemblyVersion("1.9.3.0")] -[assembly: AssemblyFileVersion("1.9.3.0")] +// [assembly: AssemblyVersion("1.9.4.0")] +[assembly: AssemblyVersion("1.9.4.0")] +[assembly: AssemblyFileVersion("1.9.4.0")] diff --git a/DesktopEdge/Views/Controls/StyledButton.xaml b/DesktopEdge/Views/Controls/StyledButton.xaml index 5bb623f8..51d11a1d 100644 --- a/DesktopEdge/Views/Controls/StyledButton.xaml +++ b/DesktopEdge/Views/Controls/StyledButton.xaml @@ -20,6 +20,11 @@ + + + + + diff --git a/DesktopEdge/Views/Controls/StyledButton.xaml.cs b/DesktopEdge/Views/Controls/StyledButton.xaml.cs index 086f0cfe..c6f44b23 100644 --- a/DesktopEdge/Views/Controls/StyledButton.xaml.cs +++ b/DesktopEdge/Views/Controls/StyledButton.xaml.cs @@ -23,6 +23,15 @@ namespace ZitiDesktopEdge { public delegate void ClickAction(); public event ClickAction OnClick; private string _label = ""; + private string bgColor = "#0069FF"; + + public string BgColor { + get { return bgColor; } + set { + bgColor = value; + ButtonBgColor.Color = (Color)ColorConverter.ConvertFromString(bgColor); + } + } public string Label { get { @@ -44,8 +53,8 @@ namespace ZitiDesktopEdge { /// The button object /// The mouse event private void Hover(object sender, MouseEventArgs e) { - ButtonBg.Opacity = 0.8; - ButtonBg.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1.0, TimeSpan.FromSeconds(.3))); + ButtonBgDarken.Opacity = 0.0; + ButtonBgDarken.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(0.2, TimeSpan.FromSeconds(.3))); } /// @@ -54,9 +63,8 @@ namespace ZitiDesktopEdge { /// The button object /// The mouse event private void Leave(object sender, MouseEventArgs e) { - ButtonBgColor.Color = Color.FromRgb(0, 104, 249); - ButtonBg.BeginAnimation(Grid.OpacityProperty, null); - ButtonBg.Opacity = 0.8; + ButtonBgDarken.Opacity = 0.2; + ButtonBgDarken.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(0.0, TimeSpan.FromSeconds(.3))); } /// @@ -65,7 +73,7 @@ namespace ZitiDesktopEdge { /// /// private void Down(object sender, MouseButtonEventArgs e) { - ButtonBgColor.Color = Color.FromRgb(126, 180, 255); + // ButtonBgColor.Color = Color.FromRgb(126, 180, 255); } /// diff --git a/DesktopEdge/Views/ItemRenderers/Filter.xaml b/DesktopEdge/Views/ItemRenderers/Filter.xaml new file mode 100644 index 00000000..f44575bf --- /dev/null +++ b/DesktopEdge/Views/ItemRenderers/Filter.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + Name + Address + Protocol + Port + + + Asc + Desc + + + + diff --git a/DesktopEdge/Views/ItemRenderers/Filter.xaml.cs b/DesktopEdge/Views/ItemRenderers/Filter.xaml.cs new file mode 100644 index 00000000..1c39c4b8 --- /dev/null +++ b/DesktopEdge/Views/ItemRenderers/Filter.xaml.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Timers; +using ZitiDesktopEdge.Models; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for Filter.xaml + /// + public partial class Filter :UserControl { + + public delegate void OnFilterEvent(FilterData filter); + public event OnFilterEvent OnFilter; + public string placeholder = "any text"; + private static Timer timeout; + + private FilterData filter = new FilterData("", "Name", "Asc"); + public Filter() { + InitializeComponent(); + } + + public void Clear() { + FilterFor.Text = ""; + filter.SearchFor = ""; + FilterFor.Text = placeholder; + SortWayField.SelectedIndex = 0; + SortByField.SelectedIndex = 0; + } + + private void FilterPressed(object sender, KeyEventArgs e) { + if (e.Key==Key.Enter) { + OnFilter?.Invoke(filter); + } + } + + private void FilterChanged(object sender, KeyEventArgs e) { + string search = FilterFor.Text.Trim(); + if (filter.SearchFor!=search) { + filter.SearchFor = search; + if (filter.SearchFor == placeholder) filter.SearchFor = ""; + + if (e.Key==Key.Enter) { + OnFilter?.Invoke(filter); + } else { + if (timeout != null && timeout.Enabled) { + timeout.Close(); + } + timeout = new Timer(1000); + timeout.Elapsed += OnTimedEvent; + timeout.AutoReset = false; + timeout.Enabled = true; + } + } + } + + private void SortWayChanged(object sender, SelectionChangedEventArgs e) { + ComboBoxItem selected = (ComboBoxItem)SortWayField.SelectedValue; + if (selected != null && selected.Content != null) { + if (selected.Content.ToString() != filter.SortHow) { + filter.SortHow = selected.Content.ToString(); + this.OnFilter?.Invoke(filter); + } + } + } + + private void SortByChanged(object sender, SelectionChangedEventArgs e) { + ComboBoxItem selected = (ComboBoxItem)SortByField.SelectedValue; + if (selected != null && selected.Content != null) { + if (selected.Content.ToString() != filter.SortBy) { + filter.SortBy = selected.Content.ToString(); + this.OnFilter?.Invoke(filter); + } + } + } + + private void FocusFilter(object sender, RoutedEventArgs e) { + if (FilterFor.Text==placeholder) { + FilterFor.Text = ""; + } + } + + private void FocusLostFilter(object sender, RoutedEventArgs e) { + if (FilterFor.Text.Trim() == "") { + FilterFor.Text = placeholder; + } + } + + private void OnTimedEvent(Object source, ElapsedEventArgs e) { + this.Dispatcher.Invoke(() => { + OnFilter?.Invoke(filter); + }); + } + } +} diff --git a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs index 1b75ed8e..32ca433e 100644 --- a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs @@ -107,8 +107,6 @@ namespace ZitiDesktopEdge { private void OpenDetails(object sender, MouseButtonEventArgs e) { IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; deets.SelectedIdentity = this; - deets.ServiceTitle.Clear(); - deets.IdDetailToggle.Enabled = this.Identity.IsEnabled; deets.Identity = this.Identity; } diff --git a/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml b/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml index 3bebb7db..2e843e7c 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml +++ b/DesktopEdge/Views/ItemRenderers/MenuEditItem.xaml @@ -15,6 +15,5 @@ - \ No newline at end of file diff --git a/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml b/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml index d40627ac..02d1bc3f 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml +++ b/DesktopEdge/Views/ItemRenderers/MenuEditSearch.xaml @@ -21,7 +21,7 @@ diff --git a/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs index 1244c1fb..36586978 100644 --- a/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/MenuIdentityItem.xaml.cs @@ -64,7 +64,6 @@ namespace ZitiDesktopEdge { private void ShowIdentity(object sender, MouseButtonEventArgs e) { IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; deets.SelectedIdentityMenu = this; - deets.IdDetailToggle.Enabled = this.Identity.IsEnabled; deets.Identity = this.Identity; } } diff --git a/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml b/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml index cbb7e818..d88c7b68 100644 --- a/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml +++ b/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml @@ -9,7 +9,8 @@ - + + @@ -17,8 +18,12 @@ - - + + + + + + diff --git a/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml.cs b/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml.cs index 4534dc1e..e0e678d5 100644 --- a/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/ServiceInfo.xaml.cs @@ -36,10 +36,41 @@ namespace ZitiDesktopEdge { } set { this._info = value; - MainEdit.ToolTip = this._info.ToString(); - MainEdit.Text = this._info.ToString(); + + string addresses = ""; + if (_info.Addresses!=null) { + for (int i = 0; i < this._info.Addresses?.Length; i++) { + addresses += ((i > 0) ? "," : "") + this._info.Addresses[i].Hostname; + } + } + + MainEdit.ToolTip = addresses; + MainEdit.Text = addresses; + MainLabel.ToolTip = this._info.Name; MainLabel.Text = this._info.Name; + + string ports = ""; + if (_info.Ports != null) { + for (int i = 0; i < this._info.Ports.Length; i++) { + ports += ((i > 0) ? "," : "") + this._info.Ports[i].ToString(); + } + } + + Ports.ToolTip = ports; + Ports.Text = ports; + + string protocols = ""; + + if (_info.Protocols != null) { + for (int i = 0; i < this._info.Protocols.Length; i++) { + protocols += ((i > 0) ? "," : "") + this._info.Protocols[i]; + } + } + + Protocols.ToolTip = protocols; + Protocols.Text = protocols; + WarningColumn.Width = new GridLength(0); if (this._info.Warning.Length > 0) { WarnIcon.ToolTip = this._info.Warning; diff --git a/DesktopEdge/Views/Screens/IdentityDetails.xaml b/DesktopEdge/Views/Screens/IdentityDetails.xaml index 408e801a..30fa1d48 100644 --- a/DesktopEdge/Views/Screens/IdentityDetails.xaml +++ b/DesktopEdge/Views/Screens/IdentityDetails.xaml @@ -6,7 +6,7 @@ xmlns:local="clr-namespace:ZitiDesktopEdge" mc:Ignorable="d" x:Name="IdentityArea" - d:DesignHeight="500" d:DesignWidth="350"> + d:DesignHeight="500" d:DesignWidth="350" IsVisibleChanged="VisibilityChanged"> @@ -16,109 +16,91 @@ - + - + - - + + - + - + - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - Name - Address - Protocol - Port - - - - - - - Asc - Desc - - - - - + + - - + @@ -149,7 +131,7 @@ - + diff --git a/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs b/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs index ebaa8140..1bd4a1f1 100644 --- a/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs +++ b/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -10,6 +11,8 @@ using System.Windows.Media.Animation; using NLog; using System.Web; +using System.Windows.Media; +using System.Windows.Data; namespace ZitiDesktopEdge { /// @@ -35,10 +38,21 @@ namespace ZitiDesktopEdge { public event OnAuthenticate Authenticate; public delegate void OnRecovery(ZitiIdentity identity); public event OnRecovery Recovery; + public delegate void LoadingEvent(bool isComplete); + public event LoadingEvent OnLoading; + public int Page = 1; public int PerPage = 50; + public int TotalPages = 1; public string SortBy = "Name"; public string SortWay = "Asc"; + private bool _loaded = false; + private double scrolledTo = 0; + public int totalServices = 0; + private ScrollViewer _scroller; + + public ObservableCollection _services = new ObservableCollection(); + public ObservableCollection ZitiServices { get { return _services; } } internal MainWindow MainWindow { get; set; } @@ -55,13 +69,15 @@ namespace ZitiDesktopEdge { return _identity; } set { + _loaded = false; + FilterServices.Clear(); + scrolledTo = 0; _identity = value; - this.IdDetailToggle.Enabled = _identity.IsEnabled; + ServiceCount.Content = _identity.Services.Count + " service"+((_identity.Services.Count>1)?"s":""); Page = 1; SortBy = "Name"; SortWay = "Asc"; - SortByField.SelectedIndex = 0; - SortWayField.SelectedIndex = 0; + filter = ""; UpdateView(); IdentityArea.Opacity = 1.0; IdentityArea.Visibility = Visibility.Visible; @@ -97,16 +113,31 @@ namespace ZitiDesktopEdge { } public void UpdateView() { + if (_scroller==null) { + _scroller = GetScrollViewer(ServiceList) as ScrollViewer; + } + if (_scroller != null) { + _scroller.InvalidateScrollInfo(); + _scroller.ScrollToVerticalOffset(0); + _scroller.InvalidateScrollInfo(); + } + scrolledTo = 0; IdDetailName.Text = _identity.Name; IdDetailName.ToolTip = _identity.Name; - IdDetailToggle.Enabled = _identity.IsEnabled; - IdentityName.Value = _identity.Name; - IdentityNetwork.Value = _identity.ControllerUrl; - IdentityEnrollment.Value = _identity.EnrollmentStatus; - IdentityStatus.Value = _identity.IsEnabled ? "active" : "disabled"; IdentityMFA.IsOn = _identity.IsMFAEnabled; IdentityMFA.ToggleField.IsEnabled = true; IdentityMFA.ToggleField.Opacity = 1; + IsConnected.Visibility = Visibility.Collapsed; + IsDisconnected.Visibility = Visibility.Collapsed; + IsConnected.ToolTip = "Enabled - Click To Disable"; + IsDisconnected.ToolTip = "Disabled - Click to Enable"; + IdServer.Value = _identity.ControllerUrl; + IdName.Value = _identity.Name; + if (_identity.IsEnabled) { + IsConnected.Visibility = Visibility.Visible; + } else { + IsDisconnected.Visibility = Visibility.Visible; + } if (_identity.IsMFAEnabled) { if (_identity.MFAInfo.IsAuthenticated) { IdentityMFA.ToggleField.IsEnabled = true; @@ -125,13 +156,16 @@ namespace ZitiDesktopEdge { IdentityMFA.AuthOff.Visibility = Visibility.Collapsed; IdentityMFA.RecoveryButton.Visibility = Visibility.Collapsed; } - ServiceList.Children.Clear(); - PageSortArea.Visibility = Visibility.Collapsed; + //ServiceList.Children.Clear(); + + totalServices = _identity.Services.Count; + if (_identity.Services.Count>0) { - PageSortArea.Visibility = Visibility.Visible; int index = 0; int total = 0; ZitiService[] services = new ZitiService[0]; + _services = null; + _services = new ObservableCollection(); if (SortBy == "Name") services = _identity.Services.OrderBy(s => s.Name.ToLower()).ToArray(); else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray(); else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray(); @@ -140,86 +174,49 @@ namespace ZitiDesktopEdge { int startIndex = (Page - 1) * PerPage; for (int i= startIndex; i= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) { - Logger.Trace("painting: " + zitiSvc.Name); - ServiceInfo info = new ServiceInfo(); - info.Info = zitiSvc; - info.OnMessage += Info_OnMessage; - info.OnDetails += ShowDetails; - ServiceList.Children.Add(info); + _services.Add(zitiSvc); index++; } } } + ServiceList.ItemsSource = ZitiServices; - if (Page==1) { - Pages.Items.Clear(); - int totalPages = (total / PerPage) + 1; - for (int i = 1; i <= totalPages; i++) { - ComboBoxItem item = new ComboBoxItem(); - if (i == Page) item.IsSelected = true; - item.Content = i.ToString(); - Pages.Items.Add(item); - } - } + TotalPages = (total / PerPage) + 1; - double newHeight = MainHeight - 330; - ServiceRow.Height = new GridLength((double)newHeight); + double newHeight = MainHeight - 330; MainDetailScroll.MaxHeight = newHeight; MainDetailScroll.Height = newHeight; MainDetailScroll.Visibility = Visibility.Visible; - ServiceTitle.Label = _identity.Services.Count + " Services"; - ServiceTitle.MainEdit.Visibility = Visibility.Visible; - ServiceTitle.Visibility = Visibility.Visible; + AuthMessageBg.Visibility = Visibility.Collapsed; AuthMessageLabel.Visibility = Visibility.Collapsed; NoAuthServices.Visibility = Visibility.Collapsed; } else { - ServiceRow.Height = new GridLength((double)0.0); MainDetailScroll.Visibility = Visibility.Collapsed; - ServiceTitle.Label = "No Services"; - ServiceTitle.MainEdit.Text = ""; - ServiceTitle.MainEdit.Visibility = Visibility.Collapsed; if (this._identity.IsMFAEnabled&&!this._identity.MFAInfo.IsAuthenticated) { - ServiceTitle.Visibility = Visibility.Collapsed; AuthMessageBg.Visibility = Visibility.Visible; AuthMessageLabel.Visibility = Visibility.Visible; - ServiceRow.Height = new GridLength((double)30); NoAuthServices.Visibility = Visibility.Visible; } else { - ServiceTitle.Visibility = Visibility.Visible; AuthMessageBg.Visibility = Visibility.Collapsed; AuthMessageLabel.Visibility = Visibility.Collapsed; NoAuthServices.Visibility = Visibility.Collapsed; } } ConfirmView.Visibility = Visibility.Collapsed; + _loaded = true; } private void ShowDetails(ZitiService info) { DetailName.Text = info.Name; + DetailProtocols.Text = info.ProtocolString; + DetailAddress.Text = info.AddressString; + DetailPorts.Text = info.PortString; DetailUrl.Text = info.ToString(); - string protocols = ""; - string addresses = ""; - string ports = ""; - - for (int i = 0; i < info.Protocols.Length; i++) { - protocols += ((i > 0) ? "," : "") + info.Protocols[i]; - } - for (int i = 0; i < info.Addresses.Length; i++) { - addresses += ((i>0)?",":"")+info.Addresses[i]; - } - for (int i = 0; i < info.Ports.Length; i++) { - ports += ((i > 0) ? "," : "") + info.Ports[i]; - } - - DetailProtocols.Text = protocols; - DetailAddress.Text = addresses; - DetailPorts.Text = ports; - DetailsArea.Visibility = Visibility.Visible; DetailsArea.Opacity = 0; DetailsArea.Margin = new Thickness(0, 0, 0, 0); @@ -257,15 +254,6 @@ namespace ZitiDesktopEdge { OnMessage?.Invoke(message); } - async private void IdToggle(bool on) { - DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; - await client.IdentityOnOffAsync(_identity.Fingerprint, on); - if (SelectedIdentity!=null) SelectedIdentity.ToggleSwitch.Enabled = on; - if (SelectedIdentityMenu != null) SelectedIdentityMenu.ToggleSwitch.Enabled = on; - _identity.IsEnabled = on; - IdentityStatus.Value = _identity.IsEnabled ? "active" : "disabled"; - } - public IdentityDetails() { InitializeComponent(); } @@ -277,7 +265,7 @@ namespace ZitiDesktopEdge { MainDetailScroll.Height = height; } - private void ForgetIdentity(object sender, MouseButtonEventArgs e) { + private void ForgetIdentity() { if (this.Visibility==Visibility.Visible&&ConfirmView.Visibility==Visibility.Collapsed) { ConfirmView.Visibility = Visibility.Visible; } @@ -313,12 +301,6 @@ namespace ZitiDesktopEdge { } } - private void DoFilter(string filter) { - this.filter = filter; - if (this._identity!=null) UpdateView(); - } - - private void ToggleMFA(bool isOn) { this.OnMFAToggled?.Invoke(isOn); } @@ -364,33 +346,112 @@ namespace ZitiDesktopEdge { this.Authenticate.Invoke(this.Identity); } - private void Pages_SelectionChanged(object sender, SelectionChangedEventArgs e) { - ComboBoxItem selected = (ComboBoxItem)Pages.SelectedValue; - if (selected!=null) { - int value = Int32.Parse(selected.Content.ToString()); - if (value!=Page) { - Page = value; - UpdateView(); + public static DependencyObject GetScrollViewer(DependencyObject o) { + if (o is ScrollViewer) { return o; } + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) { + var child = VisualTreeHelper.GetChild(o, i); + var result = GetScrollViewer(child); + if (result == null) { + continue; + } else { + return result; + } + } + return null; + } + + private void Scrolled(object sender, ScrollChangedEventArgs e) { + if (_loaded) { + if (_scroller == null) { + _scroller = GetScrollViewer(ServiceList) as ScrollViewer; + } + var verticalOffset = _scroller.VerticalOffset; + var maxVerticalOffset = _scroller.ScrollableHeight; + + if ((maxVerticalOffset < 0 || verticalOffset == maxVerticalOffset) && verticalOffset>0 && scrolledTo s.Name.ToLower()).ToArray(); + else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray(); + else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray(); + else if (SortBy == "Port") services = _identity.Services.OrderBy(s => s.Ports.ToString()).ToArray(); + if (SortWay == "Desc") services = services.Reverse().ToArray(); + int startIndex = (Page - 1) * PerPage; + for (int i = startIndex; i < services.Length; i++) { + ZitiService zitiSvc = services[i]; + if (index < PerPage) { + if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) { + ZitiServices.Add(zitiSvc); + index++; + } + } + } + double totalOffset = _scroller.VerticalOffset; + double toNegate = index * 33; + double scrollTo = (totalOffset - toNegate); + _scroller.InvalidateScrollInfo(); + _scroller.ScrollToVerticalOffset(verticalOffset); + _scroller.InvalidateScrollInfo(); + _loaded = true; + // scrollViewer.ScrollChanged += Scrolled; + } } } } - private void SortByField_SelectionChanged(object sender, SelectionChangedEventArgs e) { - ComboBoxItem selected = (ComboBoxItem)SortByField.SelectedValue; - if (selected != null && selected.Content!=null) { - if (selected.Content.ToString()!=SortBy) { - SortBy = selected.Content.ToString(); - UpdateView(); - } - } + private void DoFilter(FilterData filterData) { + filter = filterData.SearchFor; + SortBy = filterData.SortBy; + SortWay = filterData.SortHow; + UpdateView(); } - private void SortWayField_SelectionChanged(object sender, SelectionChangedEventArgs e) { - ComboBoxItem selected = (ComboBoxItem)SortWayField.SelectedValue; - if (selected != null && selected.Content != null) { - if (selected.Content.ToString() != SortWay) { - SortWay = selected.Content.ToString(); - UpdateView(); + async private void DoConnect(object sender, MouseButtonEventArgs e) { + this.OnLoading?.Invoke(false); + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + await client.IdentityOnOffAsync(_identity.Fingerprint, true); + if (SelectedIdentity != null) SelectedIdentity.ToggleSwitch.Enabled = true; + if (SelectedIdentityMenu != null) SelectedIdentityMenu.ToggleSwitch.Enabled = true; + _identity.IsEnabled = true; + IsConnected.Visibility = Visibility.Visible; + IsDisconnected.Visibility = Visibility.Collapsed; + this.OnLoading?.Invoke(true); + } + + async private void DoDisconnect(object sender, MouseButtonEventArgs e) { + this.OnLoading?.Invoke(false); + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + await client.IdentityOnOffAsync(_identity.Fingerprint, false); + if (SelectedIdentity != null) SelectedIdentity.ToggleSwitch.Enabled = false; + if (SelectedIdentityMenu != null) SelectedIdentityMenu.ToggleSwitch.Enabled = false; + _identity.IsEnabled = false; + IsConnected.Visibility = Visibility.Collapsed; + IsDisconnected.Visibility = Visibility.Visible; + this.OnLoading?.Invoke(true); + } + + private void WarnClicked(object sender, MouseButtonEventArgs e) { + ZitiService item = (ZitiService)(sender as FrameworkElement).DataContext; + OnMessage?.Invoke(item.WarningMessage); + } + + private void DetailsClicked(object sender, MouseButtonEventArgs e) { + ZitiService item = (ZitiService)(sender as FrameworkElement).DataContext; + ShowDetails(item); + } + + private void VisibilityChanged(object sender, DependencyPropertyChangedEventArgs e) { + if (this.Visibility==Visibility.Collapsed) { + ServiceList.DataContext = null; + if (_services!=null) { + _services.Clear(); + _services = null; } } } diff --git a/DesktopEdge/Views/Screens/MainMenu.xaml b/DesktopEdge/Views/Screens/MainMenu.xaml index d3a8286a..b3c566a2 100644 --- a/DesktopEdge/Views/Screens/MainMenu.xaml +++ b/DesktopEdge/Views/Screens/MainMenu.xaml @@ -7,6 +7,7 @@ mc:Ignorable="d" x:Name="MainMenuArea" d:DesignHeight="400" d:DesignWidth="400" Margin="1,1,1,1"> + @@ -93,6 +94,7 @@ + @@ -109,5 +111,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DesktopEdge/Views/Screens/MainMenu.xaml.cs b/DesktopEdge/Views/Screens/MainMenu.xaml.cs index aeb5726e..58ddab5d 100644 --- a/DesktopEdge/Views/Screens/MainMenu.xaml.cs +++ b/DesktopEdge/Views/Screens/MainMenu.xaml.cs @@ -1,472 +1,586 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Diagnostics; -using System; -using System.Reflection; -using System.Net.Mail; -using System.IO; -using NLog; - -using ZitiDesktopEdge.Models; -using ZitiDesktopEdge.DataStructures; -using ZitiDesktopEdge.ServiceClient; -using System.Configuration; - -namespace ZitiDesktopEdge { - /// - /// Interaction logic for MainMenu.xaml - /// - public partial class MainMenu : UserControl { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public delegate void AttachementChanged(bool attached); - public event AttachementChanged OnAttachmentChange; - public delegate void LogLevelChanged(string level); - public event LogLevelChanged OnLogLevelChanged; - public delegate void Detched(MouseButtonEventArgs e); - public event Detched OnDetach; - public string menuState = "Main"; - public string licenseData = "it's open source."; - public string LogLevel = ""; - private string appVersion = null; - private bool allowReleaseSelect = false; - public double MainHeight = 500; - - private bool isBeta { - get { - return Application.Current.Properties["ReleaseStream"]?.ToString() == "beta"; - } - } - - internal MainWindow MainWindow { get; set; } - - public MainMenu() { - InitializeComponent(); - Application.Current.MainWindow.Title = "Ziti Desktop Edge"; - - try { - allowReleaseSelect = bool.Parse(ConfigurationManager.AppSettings.Get("ReleaseStreamSelect")); - } catch { - //if we can't parse the config - leave it as false... - allowReleaseSelect = false; //setting it here in case anyone changes the default above - } -#if DEBUG - Debug.WriteLine("OVERRIDING allowReleaseSelect to true!"); - allowReleaseSelect = true; -#endif - appVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - LicensesItems.Text = licenseData; - // don't check from the UI any more... CheckUpdates(); - } - - private void HideMenu(object sender, MouseButtonEventArgs e) { - menuState = "Menu"; - UpdateState(); - MainMenuArea.Visibility = Visibility.Collapsed; - } - private void Window_MouseDown(object sender, MouseButtonEventArgs e) { - if (e.ChangedButton == MouseButton.Left) { - OnDetach(e); - } - } - - private void CloseApp(object sender, MouseButtonEventArgs e) { - Application.Current.Shutdown(); - } - - private void ShowAbout(object sender, MouseButtonEventArgs e) { - menuState = "About"; - UpdateState(); - } - - private void ShowAdvanced(object sender, MouseButtonEventArgs e) { - menuState = "Advanced"; - UpdateState(); - } - private void ShowIdentities(object sender, MouseButtonEventArgs e) { - menuState = "Identities"; - UpdateState(); - } - private void ShowLicenses(object sender, MouseButtonEventArgs e) { - menuState = "Licenses"; - UpdateState(); - } - private void ShowConfig(object sender, MouseButtonEventArgs e) { - menuState = "Config"; - UpdateState(); - } - private void ShowLogs(object sender, MouseButtonEventArgs e) { - menuState = "Logs"; - UpdateState(); - } - private void ShowUILogs(object sender, MouseButtonEventArgs e) { - menuState = "UILogs"; - UpdateState(); - } - private void ShowReleaseStreamMenuAction(object sender, MouseButtonEventArgs e) { - logger.Warn("this is ShowReleaseStreamMenuAction at warn"); - logger.Info("this is ShowReleaseStreamMenuAction at info"); - logger.Debug("this is ShowReleaseStreamMenuAction at debug"); - logger.Trace("this is ShowReleaseStreamMenuAction at trace"); - menuState = "SetReleaseStream"; - UpdateState(); - } - - async private void SetReleaseStreamMenuAction(object sender, MouseButtonEventArgs e) { - CheckForUpdateStatus.Visibility = Visibility.Collapsed; - TriggerUpdateButton.Visibility = Visibility.Collapsed; - SubOptionItem opt = (SubOptionItem)sender; - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - menuState = "SetReleaseStream"; - - bool releaseClicked = opt.Label.ToLower() == "stable"; - - if (releaseClicked) { - if (isBeta) { - //toggle to stable - var r = await monitorClient.SetReleaseStreamAsync("stable"); - checkResponse(r, "Error When Setting Release Stream", "An error occurred while trying to set the release stream."); - } else { - logger.Debug("stable clicked but already on stable stream"); - } - } else { - if (!isBeta) { - //toggle to beta - var r = await monitorClient.SetReleaseStreamAsync("beta"); - checkResponse(r, "Error When Setting Release Stream", "An error occurred while trying to set the release stream."); - } else { - logger.Debug("beta clicked but already on beta stream"); - } - } - Application.Current.Properties["ReleaseStream"] = opt.Label.ToLower(); - UpdateState(); - } - - private void checkResponse(SvcResponse r, string titleOnErr, string msgOnErr) { - if (r == null) { - MainWindow.ShowError(titleOnErr, msgOnErr); - } else { - logger.Info(r?.ToString()); - } - } - - private void SetLogLevel(object sender, MouseButtonEventArgs e) { - menuState = "LogLevel"; - UpdateState(); - } - - private void UpdateState() { - IdListScrollView.Height = this.ActualHeight-100.00; - IdListScrollView.Visibility = Visibility.Collapsed; - MainItems.Visibility = Visibility.Collapsed; - AboutItems.Visibility = Visibility.Collapsed; - MainItemsButton.Visibility = Visibility.Collapsed; - AboutItemsArea.Visibility = Visibility.Collapsed; - BackArrow.Visibility = Visibility.Collapsed; - AdvancedItems.Visibility = Visibility.Collapsed; - LicensesItems.Visibility = Visibility.Collapsed; - LogsItems.Visibility = Visibility.Collapsed; - ConfigItems.Visibility = Visibility.Collapsed; - LogLevelItems.Visibility = Visibility.Collapsed; - ReleaseStreamItems.Visibility = Visibility.Collapsed; - - if (menuState == "About") { - MenuTitle.Content = "About"; - AboutItemsArea.Visibility = Visibility.Visible; - AboutItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - - string version = ""; - try { - TunnelStatus s = (TunnelStatus)Application.Current.Properties["CurrentTunnelStatus"]; - version = $"{s.ServiceVersion.Version}@{s.ServiceVersion.Revision}"; - } catch (Exception e) { - logger.Warn(e, "Could not get service version/revision?"); - } - - // Interface Version - VersionInfo.Content = $"App: {appVersion} Service: {version}"; - - } else if (menuState == "Advanced") { - MenuTitle.Content = "Advanced Settings"; - AdvancedItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - ShowReleaseStreamMenuItem.Visibility = allowReleaseSelect ? Visibility.Visible : Visibility.Collapsed; - } else if (menuState == "Licenses") { - MenuTitle.Content = "Third Party Licenses"; - LicensesItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "Logs") { - MenuTitle.Content = "Advanced Settings"; - AdvancedItems.Visibility = Visibility.Visible; - //string targetFile = NativeMethods.GetFinalPathName(MainWindow.ExpectedLogPathServices); - string targetFile = MainWindow.ExpectedLogPathServices; - - OpenLogFile("service", targetFile); - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "UILogs") { - MenuTitle.Content = "Advanced Settings"; - AdvancedItems.Visibility = Visibility.Visible; - OpenLogFile("UI", MainWindow.ExpectedLogPathUI); - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "LogLevel") { - ResetLevels(); - - MenuTitle.Content = "Set Log Level"; - LogLevelItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "SetReleaseStream") { - SetReleaseStream(); - - MenuTitle.Content = "Set Release Stream"; - ReleaseStreamItems.Visibility = allowReleaseSelect ? Visibility.Visible : Visibility.Collapsed; - BackArrow.Visibility = Visibility.Visible; - } else if (menuState == "Config") { - MenuTitle.Content = "Tunnel Configuration"; - ConfigItems.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - - ConfigIp.Value = Application.Current.Properties["ip"]?.ToString(); - ConfigSubnet.Value = Application.Current.Properties["subnet"]?.ToString(); - ConfigMtu.Value = Application.Current.Properties["mtu"]?.ToString(); - ConfigDns.Value = (bool)Application.Current.Properties["dnsenabled"] ? Application.Current.Properties["dns"]?.ToString() : "disabled"; - } else if (menuState == "Identities") { - MenuTitle.Content = "Identities"; - IdListScrollView.Visibility = Visibility.Visible; - BackArrow.Visibility = Visibility.Visible; - } else { - MenuTitle.Content = "Main Menu"; - MainItems.Visibility = Visibility.Visible; - MainItemsButton.Visibility = Visibility.Visible; - ReleaseStreamItems.Visibility = Visibility.Collapsed; - } - } - - private void OpenLogFile(string which, string logFile) { - var whichRoot = Path.Combine(MainWindow.ExpectedLogPathRoot, which); - try { - string target = Native.NativeMethods.GetFinalPathName(logFile); - if (File.Exists(target)) { - logger.Info("opening {0} logs at: {1}", which, target); - var p = Process.Start(new ProcessStartInfo(target) { UseShellExecute = true }); - if (p != null) { - logger.Info("showing {0} logs. file: {1}", which, target); - } else { - Process.Start(whichRoot); - } - return; - } else { - logger.Warn("could not show {0} logs. file not found: {1}", which, target); - } - } catch { - } - Process.Start(whichRoot); - } - - private void GoBack(object sender, MouseButtonEventArgs e) { - if (menuState == "Config" || menuState == "LogLevel" || menuState == "UILogs" || menuState == "SetReleaseStream") { - menuState = "Advanced"; - } else if (menuState == "Licenses") { - menuState = "About"; - } else { - menuState = "Menu"; - } - UpdateState(); - } - private void ShowPrivacy(object sender, MouseButtonEventArgs e) { - Process.Start(new ProcessStartInfo("https://netfoundry.io/privacy") { UseShellExecute = true }); - } - private void ShowTerms(object sender, MouseButtonEventArgs e) { - Process.Start(new ProcessStartInfo("https://netfoundry.io/terms") { UseShellExecute = true }); - } - - async private void ShowFeedback(object sender, MouseButtonEventArgs e) { - try { - MainWindow.ShowLoad("Collecting Information", "Please wait while we run some commands\nand collect some diagnostic information"); - var mailMessage = new MailMessage("help@openziti.org", "help@openziti.org"); - mailMessage.Subject = "Ziti Support"; - mailMessage.IsBodyHtml = false; - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - sb.Append("Logs collected at : " + DateTime.Now.ToString()); - sb.Append(". client version : " + appVersion); - - mailMessage.Body = sb.ToString(); - - string timestamp = DateTime.Now.ToFileTime().ToString(); - - var dataClient = (DataClient)Application.Current.Properties["ServiceClient"]; - await dataClient.zitiDump(); - - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); - if (resp == null) { - logger.Error("no response from monitorClient?"); - MainWindow mw = (MainWindow)Application.Current.MainWindow; - mw?.ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback.\nIs the monitor service running?"); - return; - } - string pathToLogs = resp.Message; - logger.Info("Log files found at : {0}", resp.Message); - mailMessage.Attachments.Add(new Attachment(pathToLogs)); - - string emlFile = Path.Combine(Path.GetTempPath(), timestamp + "-ziti.eml"); - - using (var filestream = File.Open(emlFile, FileMode.Create)) { - var binaryWriter = new BinaryWriter(filestream); - binaryWriter.Write(System.Text.Encoding.UTF8.GetBytes("X-Unsent: 1" + Environment.NewLine)); - var assembly = typeof(SmtpClient).Assembly; - var mailWriterType = assembly.GetType("System.Net.Mail.MailWriter"); - var mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null); - var mailWriter = mailWriterContructor.Invoke(new object[] { filestream }); - var sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic); - sendMethod.Invoke(mailMessage, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { mailWriter, true, true }, null); - var closeMethod = mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic); - closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null); - } - var p = Process.Start(emlFile); - if (p != null) { - p.Exited += (object lambdaSender, EventArgs lambdaEventArgs) => { - logger.Info("Removing temp file: {0}", emlFile); - File.Delete(emlFile); - }; - p.EnableRaisingEvents = true; - } else { - logger.Debug("process was null. most likely the email file format was not known when the process tried to start"); - } - } catch (Exception ex) { - logger.Warn(ex, "An unexpected error has occurred when submitting feedback? {0}", ex.Message); - } - MainWindow.HideLoad(); - } - - private void ShowSupport(object sender, MouseButtonEventArgs e) { - Process.Start(new ProcessStartInfo("https://openziti.discourse.group/") { UseShellExecute = true }); - } - - private void DetachWindow(object sender, MouseButtonEventArgs e) { - Application.Current.MainWindow.ShowInTaskbar = true; - DetachButton.Visibility = Visibility.Collapsed; - AttachButton.Visibility = Visibility.Visible; - Arrow.Visibility = Visibility.Collapsed; - if (OnAttachmentChange != null) { - OnAttachmentChange(false); - } - MainMenuArea.Visibility = Visibility.Collapsed; - } - - public void Detach() { - Application.Current.MainWindow.ShowInTaskbar = true; - DetachButton.Visibility = Visibility.Collapsed; - AttachButton.Visibility = Visibility.Visible; - Arrow.Visibility = Visibility.Collapsed; - } - private void RetachWindow(object sender, MouseButtonEventArgs e) { - Application.Current.MainWindow.ShowInTaskbar = false; - DetachButton.Visibility = Visibility.Visible; - AttachButton.Visibility = Visibility.Collapsed; - Arrow.Visibility = Visibility.Visible; - if (OnAttachmentChange != null) { - OnAttachmentChange(true); - } - } - - private void ResetLevels() { - if (this.LogLevel == "") this.LogLevel = "error"; - LogVerbose.IsSelected = false; - LogDebug.IsSelected = false; - LogInfo.IsSelected = false; - LogError.IsSelected = false; - LogFatal.IsSelected = false; - LogWarn.IsSelected = false; - LogTrace.IsSelected = false; - if (this.LogLevel == "verbose") LogVerbose.IsSelected = true; - else if (this.LogLevel == "debug") LogDebug.IsSelected = true; - else if (this.LogLevel == "info") LogInfo.IsSelected = true; - else if (this.LogLevel == "error") LogError.IsSelected = true; - else if (this.LogLevel == "fatal") LogFatal.IsSelected = true; - else if (this.LogLevel == "warn") LogWarn.IsSelected = true; - else if (this.LogLevel == "trace") LogTrace.IsSelected = true; - } - - private void SetLevel(object sender, MouseButtonEventArgs e) { - SubOptionItem item = (SubOptionItem)sender; - this.LogLevel = item.Label.ToLower(); - if (OnLogLevelChanged != null) { - OnLogLevelChanged(this.LogLevel); - } - ResetLevels(); - } - - private void SetReleaseStream() { - this.ReleaseStreamItemBeta.IsSelected = isBeta; - this.ReleaseStreamItemStable.IsSelected = !isBeta; - } - - async private void CheckForUpdate_Click(object sender, RoutedEventArgs e) { - logger.Info("checking for update..."); - try { - CheckForUpdate.IsEnabled = false; - CheckForUpdateStatus.Content = "Checking for update..."; - CheckForUpdateStatus.Visibility = Visibility.Visible; - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - var r = await monitorClient.DoUpdateCheck(); - checkResponse(r, "Error When Checking for Update", "An error occurred while trying check for update."); - CheckForUpdateStatus.Content = r.Message; - if (r.UpdateAvailable) { - TriggerUpdateButton.Visibility = Visibility.Visible; - } else { - TriggerUpdateButton.Visibility = Visibility.Collapsed; - } - } catch (Exception ex) { - logger.Error(ex, "unexpected error in update check: {0}", ex.Message); - } - CheckForUpdate.IsEnabled = true; - } - - async private void TriggerUpdate_Click(object sender, RoutedEventArgs e) { - try { - CheckForUpdate.IsEnabled = false; - TriggerUpdateButton.IsEnabled = false; - CheckForUpdateStatus.Content = "Requesting automatic update..."; - var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; - var r = await monitorClient.TriggerUpdate(); - CheckForUpdateStatus.Content = "Automatic update requested..."; - checkResponse(r, "Error When Triggering Update", "An error occurred while trying to trigger the update."); - } catch (Exception ex) { - logger.Error(ex, "unexpected error in update check: {0}", ex.Message); - } - TriggerUpdateButton.IsEnabled = true; - CheckForUpdate.IsEnabled = true; - } - - public void SetupIdList(ZitiIdentity[] ids) { - IdListView.Children.Clear(); - for (int i=0; i { - VersionOlder.Content = msg; - VersionNewer.Content = ""; - VersionOlder.Visibility = Visibility.Visible; - VersionNewer.Visibility = Visibility.Collapsed; - }); - } - public void SetAppIsNewer(string msg) { - this.Dispatcher.Invoke(() => { - VersionNewer.Content = msg; - VersionOlder.Content = ""; - VersionNewer.Visibility = Visibility.Visible; - VersionOlder.Visibility = Visibility.Collapsed; - }); - } - } +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Diagnostics; +using System; +using System.Reflection; +using System.Net.Mail; +using System.IO; +using NLog; +using System.Windows.Media.Animation; + +using ZitiDesktopEdge.Models; +using ZitiDesktopEdge.DataStructures; +using ZitiDesktopEdge.ServiceClient; +using System.Configuration; + +namespace ZitiDesktopEdge { + /// + /// Interaction logic for MainMenu.xaml + /// + public partial class MainMenu : UserControl { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public delegate void AttachementChanged(bool attached); + public event AttachementChanged OnAttachmentChange; + public delegate void LogLevelChanged(string level); + public event LogLevelChanged OnLogLevelChanged; + public delegate void Detched(MouseButtonEventArgs e); + public event Detched OnDetach; + public delegate void ShowBlurb(string message); + public event ShowBlurb OnShowBlurb; + public string menuState = "Main"; + public string licenseData = "it's open source."; + public string LogLevel = ""; + private string appVersion = null; + private bool allowReleaseSelect = false; + public double MainHeight = 500; + + private bool isBeta { + get { + return Application.Current.Properties["ReleaseStream"]?.ToString() == "beta"; + } + } + + internal MainWindow MainWindow { get; set; } + + public MainMenu() { + InitializeComponent(); + Application.Current.MainWindow.Title = "Ziti Desktop Edge"; + + try { + allowReleaseSelect = bool.Parse(ConfigurationManager.AppSettings.Get("ReleaseStreamSelect")); + } catch { + //if we can't parse the config - leave it as false... + allowReleaseSelect = false; //setting it here in case anyone changes the default above + } +#if DEBUG + Debug.WriteLine("OVERRIDING allowReleaseSelect to true!"); + allowReleaseSelect = true; +#endif + appVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + LicensesItems.Text = licenseData; + // don't check from the UI any more... CheckUpdates(); + } + + private void HideMenu(object sender, MouseButtonEventArgs e) { + menuState = "Menu"; + UpdateState(); + MainMenuArea.Visibility = Visibility.Collapsed; + } + private void Window_MouseDown(object sender, MouseButtonEventArgs e) { + if (e.ChangedButton == MouseButton.Left) { + OnDetach(e); + } + } + + private void CloseApp(object sender, MouseButtonEventArgs e) { + Application.Current.Shutdown(); + } + + private void ShowAbout(object sender, MouseButtonEventArgs e) { + menuState = "About"; + UpdateState(); + } + + private void ShowAdvanced(object sender, MouseButtonEventArgs e) { + menuState = "Advanced"; + UpdateState(); + } + private void ShowIdentities(object sender, MouseButtonEventArgs e) { + menuState = "Identities"; + UpdateState(); + } + private void ShowLicenses(object sender, MouseButtonEventArgs e) { + menuState = "Licenses"; + UpdateState(); + } + private void ShowConfig(object sender, MouseButtonEventArgs e) { + menuState = "Config"; + UpdateState(); + } + private void ShowLogs(object sender, MouseButtonEventArgs e) { + menuState = "Logs"; + UpdateState(); + } + private void ShowUILogs(object sender, MouseButtonEventArgs e) { + menuState = "UILogs"; + UpdateState(); + } + private void ShowReleaseStreamMenuAction(object sender, MouseButtonEventArgs e) { + logger.Warn("this is ShowReleaseStreamMenuAction at warn"); + logger.Info("this is ShowReleaseStreamMenuAction at info"); + logger.Debug("this is ShowReleaseStreamMenuAction at debug"); + logger.Trace("this is ShowReleaseStreamMenuAction at trace"); + menuState = "SetReleaseStream"; + UpdateState(); + } + + async private void SetReleaseStreamMenuAction(object sender, MouseButtonEventArgs e) { + CheckForUpdateStatus.Visibility = Visibility.Collapsed; + TriggerUpdateButton.Visibility = Visibility.Collapsed; + SubOptionItem opt = (SubOptionItem)sender; + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + menuState = "SetReleaseStream"; + + bool releaseClicked = opt.Label.ToLower() == "stable"; + + if (releaseClicked) { + if (isBeta) { + //toggle to stable + var r = await monitorClient.SetReleaseStreamAsync("stable"); + checkResponse(r, "Error When Setting Release Stream", "An error occurred while trying to set the release stream."); + } else { + logger.Debug("stable clicked but already on stable stream"); + } + } else { + if (!isBeta) { + //toggle to beta + var r = await monitorClient.SetReleaseStreamAsync("beta"); + checkResponse(r, "Error When Setting Release Stream", "An error occurred while trying to set the release stream."); + } else { + logger.Debug("beta clicked but already on beta stream"); + } + } + Application.Current.Properties["ReleaseStream"] = opt.Label.ToLower(); + UpdateState(); + } + + private void checkResponse(SvcResponse r, string titleOnErr, string msgOnErr) { + if (r == null) { + MainWindow.ShowError(titleOnErr, msgOnErr); + } else { + logger.Info(r?.ToString()); + } + } + + private void SetLogLevel(object sender, MouseButtonEventArgs e) { + menuState = "LogLevel"; + UpdateState(); + } + + private void UpdateState() { + IdListScrollView.Height = this.ActualHeight-100.00; + IdListScrollView.Visibility = Visibility.Collapsed; + MainItems.Visibility = Visibility.Collapsed; + AboutItems.Visibility = Visibility.Collapsed; + MainItemsButton.Visibility = Visibility.Collapsed; + AboutItemsArea.Visibility = Visibility.Collapsed; + BackArrow.Visibility = Visibility.Collapsed; + AdvancedItems.Visibility = Visibility.Collapsed; + LicensesItems.Visibility = Visibility.Collapsed; + LogsItems.Visibility = Visibility.Collapsed; + ConfigItems.Visibility = Visibility.Collapsed; + LogLevelItems.Visibility = Visibility.Collapsed; + ReleaseStreamItems.Visibility = Visibility.Collapsed; + + if (menuState == "About") { + MenuTitle.Content = "About"; + AboutItemsArea.Visibility = Visibility.Visible; + AboutItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + + string version = ""; + try { + TunnelStatus s = (TunnelStatus)Application.Current.Properties["CurrentTunnelStatus"]; + version = $"{s.ServiceVersion.Version}@{s.ServiceVersion.Revision}"; + } catch (Exception e) { + logger.Warn(e, "Could not get service version/revision?"); + } + + // Interface Version + VersionInfo.Content = $"App: {appVersion} Service: {version}"; + + } else if (menuState == "Advanced") { + MenuTitle.Content = "Advanced Settings"; + AdvancedItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + ShowReleaseStreamMenuItem.Visibility = allowReleaseSelect ? Visibility.Visible : Visibility.Collapsed; + } else if (menuState == "Licenses") { + MenuTitle.Content = "Third Party Licenses"; + LicensesItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "Logs") { + MenuTitle.Content = "Advanced Settings"; + AdvancedItems.Visibility = Visibility.Visible; + //string targetFile = NativeMethods.GetFinalPathName(MainWindow.ExpectedLogPathServices); + string targetFile = MainWindow.ExpectedLogPathServices; + + OpenLogFile("service", targetFile); + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "UILogs") { + MenuTitle.Content = "Advanced Settings"; + AdvancedItems.Visibility = Visibility.Visible; + OpenLogFile("UI", MainWindow.ExpectedLogPathUI); + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "LogLevel") { + ResetLevels(); + + MenuTitle.Content = "Set Log Level"; + LogLevelItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "SetReleaseStream") { + SetReleaseStream(); + + MenuTitle.Content = "Set Release Stream"; + ReleaseStreamItems.Visibility = allowReleaseSelect ? Visibility.Visible : Visibility.Collapsed; + BackArrow.Visibility = Visibility.Visible; + } else if (menuState == "Config") { + MenuTitle.Content = "Tunnel Configuration"; + ConfigItems.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + + ConfigIp.Value = Application.Current.Properties["ip"]?.ToString(); + ConfigSubnet.Value = Application.Current.Properties["subnet"]?.ToString(); + ConfigMtu.Value = Application.Current.Properties["mtu"]?.ToString(); + ConfigDns.Value = (bool)Application.Current.Properties["dnsenabled"] ? Application.Current.Properties["dns"]?.ToString() : "disabled"; + } else if (menuState == "Identities") { + MenuTitle.Content = "Identities"; + IdListScrollView.Visibility = Visibility.Visible; + BackArrow.Visibility = Visibility.Visible; + } else { + MenuTitle.Content = "Main Menu"; + MainItems.Visibility = Visibility.Visible; + MainItemsButton.Visibility = Visibility.Visible; + ReleaseStreamItems.Visibility = Visibility.Collapsed; + } + } + + private void OpenLogFile(string which, string logFile) { + var whichRoot = Path.Combine(MainWindow.ExpectedLogPathRoot, which); + try { + string target = Native.NativeMethods.GetFinalPathName(logFile); + if (File.Exists(target)) { + logger.Info("opening {0} logs at: {1}", which, target); + var p = Process.Start(new ProcessStartInfo(target) { UseShellExecute = true }); + if (p != null) { + logger.Info("showing {0} logs. file: {1}", which, target); + } else { + Process.Start(whichRoot); + } + return; + } else { + logger.Warn("could not show {0} logs. file not found: {1}", which, target); + } + } catch { + } + Process.Start(whichRoot); + } + + private void GoBack(object sender, MouseButtonEventArgs e) { + if (menuState == "Config" || menuState == "LogLevel" || menuState == "UILogs" || menuState == "SetReleaseStream") { + menuState = "Advanced"; + } else if (menuState == "Licenses") { + menuState = "About"; + } else { + menuState = "Menu"; + } + UpdateState(); + } + private void ShowPrivacy(object sender, MouseButtonEventArgs e) { + Process.Start(new ProcessStartInfo("https://netfoundry.io/privacy") { UseShellExecute = true }); + } + private void ShowTerms(object sender, MouseButtonEventArgs e) { + Process.Start(new ProcessStartInfo("https://netfoundry.io/terms") { UseShellExecute = true }); + } + + async private void ShowFeedback(object sender, MouseButtonEventArgs e) { + try { + MainWindow.ShowLoad("Collecting Information", "Please wait while we run some commands\nand collect some diagnostic information"); + var mailMessage = new MailMessage("help@openziti.org", "help@openziti.org"); + mailMessage.Subject = "Ziti Support"; + mailMessage.IsBodyHtml = false; + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + sb.Append("Logs collected at : " + DateTime.Now.ToString()); + sb.Append(". client version : " + appVersion); + + mailMessage.Body = sb.ToString(); + + string timestamp = DateTime.Now.ToFileTime().ToString(); + + var dataClient = (DataClient)Application.Current.Properties["ServiceClient"]; + await dataClient.zitiDump(); + + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + MonitorServiceStatusEvent resp = await monitorClient.CaptureLogsAsync(); + if (resp == null) { + logger.Error("no response from monitorClient?"); + MainWindow mw = (MainWindow)Application.Current.MainWindow; + mw?.ShowError("Error Collecting Feedback", "An error occurred while trying to gather feedback.\nIs the monitor service running?"); + return; + } + string pathToLogs = resp.Message; + logger.Info("Log files found at : {0}", resp.Message); + mailMessage.Attachments.Add(new Attachment(pathToLogs)); + + string emlFile = Path.Combine(Path.GetTempPath(), timestamp + "-ziti.eml"); + + using (var filestream = File.Open(emlFile, FileMode.Create)) { + var binaryWriter = new BinaryWriter(filestream); + binaryWriter.Write(System.Text.Encoding.UTF8.GetBytes("X-Unsent: 1" + Environment.NewLine)); + var assembly = typeof(SmtpClient).Assembly; + var mailWriterType = assembly.GetType("System.Net.Mail.MailWriter"); + var mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null); + var mailWriter = mailWriterContructor.Invoke(new object[] { filestream }); + var sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic); + sendMethod.Invoke(mailMessage, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { mailWriter, true, true }, null); + var closeMethod = mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic); + closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null); + } + var p = Process.Start(emlFile); + if (p != null) { + p.Exited += (object lambdaSender, EventArgs lambdaEventArgs) => { + logger.Info("Removing temp file: {0}", emlFile); + File.Delete(emlFile); + }; + p.EnableRaisingEvents = true; + } else { + logger.Debug("process was null. most likely the email file format was not known when the process tried to start"); + } + } catch (Exception ex) { + logger.Warn(ex, "An unexpected error has occurred when submitting feedback? {0}", ex.Message); + } + MainWindow.HideLoad(); + } + + private void ShowSupport(object sender, MouseButtonEventArgs e) { + Process.Start(new ProcessStartInfo("https://openziti.discourse.group/") { UseShellExecute = true }); + } + + private void DetachWindow(object sender, MouseButtonEventArgs e) { + Application.Current.MainWindow.ShowInTaskbar = true; + DetachButton.Visibility = Visibility.Collapsed; + AttachButton.Visibility = Visibility.Visible; + Arrow.Visibility = Visibility.Collapsed; + if (OnAttachmentChange != null) { + OnAttachmentChange(false); + } + MainMenuArea.Visibility = Visibility.Collapsed; + } + + public void Detach() { + Application.Current.MainWindow.ShowInTaskbar = true; + DetachButton.Visibility = Visibility.Collapsed; + AttachButton.Visibility = Visibility.Visible; + Arrow.Visibility = Visibility.Collapsed; + } + private void RetachWindow(object sender, MouseButtonEventArgs e) { + Application.Current.MainWindow.ShowInTaskbar = false; + DetachButton.Visibility = Visibility.Visible; + AttachButton.Visibility = Visibility.Collapsed; + Arrow.Visibility = Visibility.Visible; + if (OnAttachmentChange != null) { + OnAttachmentChange(true); + } + } + + private void ResetLevels() { + if (this.LogLevel == "") this.LogLevel = "error"; + LogVerbose.IsSelected = false; + LogDebug.IsSelected = false; + LogInfo.IsSelected = false; + LogError.IsSelected = false; + LogFatal.IsSelected = false; + LogWarn.IsSelected = false; + LogTrace.IsSelected = false; + if (this.LogLevel == "verbose") LogVerbose.IsSelected = true; + else if (this.LogLevel == "debug") LogDebug.IsSelected = true; + else if (this.LogLevel == "info") LogInfo.IsSelected = true; + else if (this.LogLevel == "error") LogError.IsSelected = true; + else if (this.LogLevel == "fatal") LogFatal.IsSelected = true; + else if (this.LogLevel == "warn") LogWarn.IsSelected = true; + else if (this.LogLevel == "trace") LogTrace.IsSelected = true; + } + + private void SetLevel(object sender, MouseButtonEventArgs e) { + SubOptionItem item = (SubOptionItem)sender; + this.LogLevel = item.Label.ToLower(); + if (OnLogLevelChanged != null) { + OnLogLevelChanged(this.LogLevel); + } + ResetLevels(); + } + + private void SetReleaseStream() { + this.ReleaseStreamItemBeta.IsSelected = isBeta; + this.ReleaseStreamItemStable.IsSelected = !isBeta; + } + + async private void CheckForUpdate_Click(object sender, RoutedEventArgs e) { + logger.Info("checking for update..."); + try { + CheckForUpdate.IsEnabled = false; + CheckForUpdateStatus.Content = "Checking for update..."; + CheckForUpdateStatus.Visibility = Visibility.Visible; + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + var r = await monitorClient.DoUpdateCheck(); + checkResponse(r, "Error When Checking for Update", "An error occurred while trying check for update."); + CheckForUpdateStatus.Content = r.Message; + if (r.UpdateAvailable) { + TriggerUpdateButton.Visibility = Visibility.Visible; + } else { + TriggerUpdateButton.Visibility = Visibility.Collapsed; + } + } catch (Exception ex) { + logger.Error(ex, "unexpected error in update check: {0}", ex.Message); + } + CheckForUpdate.IsEnabled = true; + } + + async public void TriggerUpdate_Click(object sender, RoutedEventArgs e) { + try { + CheckForUpdate.IsEnabled = false; + TriggerUpdateButton.IsEnabled = false; + CheckForUpdateStatus.Content = "Requesting automatic update..."; + var monitorClient = (MonitorClient)Application.Current.Properties["MonitorClient"]; + var r = await monitorClient.TriggerUpdate(); + CheckForUpdateStatus.Content = "Automatic update requested..."; + checkResponse(r, "Error When Triggering Update", "An error occurred while trying to trigger the update."); + } catch (Exception ex) { + logger.Error(ex, "unexpected error in update check: {0}", ex.Message); + } + TriggerUpdateButton.IsEnabled = true; + CheckForUpdate.IsEnabled = true; + } + + public void SetupIdList(ZitiIdentity[] ids) { + IdListView.Children.Clear(); + for (int i=0; i { + VersionOlder.Content = msg; + VersionNewer.Content = ""; + VersionOlder.Visibility = Visibility.Visible; + VersionNewer.Visibility = Visibility.Collapsed; + }); + } + public void SetAppIsNewer(string msg) { + this.Dispatcher.Invoke(() => { + VersionNewer.Content = msg; + VersionOlder.Content = ""; + VersionNewer.Visibility = Visibility.Visible; + VersionOlder.Visibility = Visibility.Collapsed; + }); + } + + /// + /// Save the config information to the properties and queue for update. + /// + async private void UpdateConfig() { + logger.Info("updating config..."); + DataClient client = (DataClient)Application.Current.Properties["ServiceClient"]; + try { + ComboBoxItem item = (ComboBoxItem)ConfigMaskNew.SelectedValue; + var newMaskVar = Int32.Parse(item.Tag.ToString()); + var addDnsNewVar = Convert.ToBoolean(AddDnsNew.IsChecked); + + var r = await client.UpdateConfigAsync(ConfigIpNew.Text, newMaskVar, addDnsNewVar); + if (r.Code != 0) { + this.OnShowBlurb?.Invoke("Error: " + r.Error); + logger.Debug("ERROR: {0} : {1}", r.Message, r.Error); + } else { + this.OnShowBlurb?.Invoke("Config Save, Please Restart Ziti to Update"); + this.CloseEdit(); + } + logger.Info("Got response from update config task : {0}", r); + } catch (DataStructures.ServiceException se) { + this.OnShowBlurb?.Invoke("Error: " + se.Message); + logger.Error(se, "service exception in update check: {0}", se.Message); + } catch (Exception ex) { + this.OnShowBlurb?.Invoke("Error: " + ex.Message); + logger.Error(ex, "unexpected error in update check: {0}", ex.Message); + } + } + + /// + /// Show the Edit Modal and blur the background + /// + private void ShowEdit() { + ConfigIpNew.Text = ConfigIp.Value; + for (int i=0; i0) { + ConfigMaskNew.SelectedIndex = i; + break; + } + } + EditArea.Opacity = 0; + EditArea.Visibility = Visibility.Visible; + EditArea.Margin = new Thickness(0, 0, 0, 0); + EditArea.BeginAnimation(Grid.OpacityProperty, new DoubleAnimation(1, TimeSpan.FromSeconds(.3))); + EditArea.BeginAnimation(Grid.MarginProperty, new ThicknessAnimation(new Thickness(30, 30, 30, 30), TimeSpan.FromSeconds(.3))); + ShowModal(); + } + + /// + /// Hide the Edit Config + /// + private void CloseEdit() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); + animation.Completed += CloseComplete; + EditArea.BeginAnimation(Grid.OpacityProperty, animation); + EditArea.BeginAnimation(Grid.MarginProperty, animateThick); + HideModal(); + } + + /// + /// Show the modal, aniimating opacity + /// + private void ShowModal() { + ModalBg.Visibility = Visibility.Visible; + ModalBg.Opacity = 0; + DoubleAnimation animation = new DoubleAnimation(.8, TimeSpan.FromSeconds(.3)); + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// Close the config window + /// + /// The close button + /// The event arguments + private void CloseComplete(object sender, EventArgs e) { + EditArea.Visibility = Visibility.Collapsed; + } + + /// + /// Hide the modal animating the opacity + /// + private void HideModal() { + DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); + animation.Completed += ModalHideComplete; + ModalBg.BeginAnimation(Grid.OpacityProperty, animation); + } + + /// + /// When the animation completes, set the visibility to avoid UI object conflicts + /// + /// The animation + /// The event + private void ModalHideComplete(object sender, EventArgs e) { + ModalBg.Visibility = Visibility.Collapsed; + } + + /// + /// Close the config editor without saving + /// + /// The image button + /// The click event + private void CloseEditConfig(object sender, MouseButtonEventArgs e) { + CloseEdit(); + } + + private void SaveConfig() { + this.UpdateConfig(); + } + } } \ No newline at end of file diff --git a/DesktopEdge/ZitiDesktopEdge.csproj b/DesktopEdge/ZitiDesktopEdge.csproj index b406e57b..a6ef0dc4 100644 --- a/DesktopEdge/ZitiDesktopEdge.csproj +++ b/DesktopEdge/ZitiDesktopEdge.csproj @@ -143,11 +143,15 @@ MSBuild:Compile Designer + StyledButton.xaml + + Filter.xaml + MenuEditSearch.xaml @@ -197,6 +201,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -447,5 +455,10 @@ + + + + + \ No newline at end of file diff --git a/Installer/ZitiDesktopEdge.aip b/Installer/ZitiDesktopEdge.aip index fff0961b..9da73f34 100644 --- a/Installer/ZitiDesktopEdge.aip +++ b/Installer/ZitiDesktopEdge.aip @@ -23,10 +23,10 @@ - + - + diff --git a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs index c64191bc..5936e0c0 100644 --- a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs +++ b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs @@ -459,4 +459,27 @@ namespace ZitiDesktopEdge.DataStructures { public class MfaRecoveryCodesResponse : SvcResponse { public string[] Payload { get; set; } } + + public class ConfigPayload + { + public string TunIPv4 { get; set; } + public int TunIPv4Mask { get; set; } + public bool AddDns { get; set; } + } + + public class ConfigUpdateFunction : ServiceFunction + { + public ConfigUpdateFunction(string tunIPv4, int tunIPv4Mask, bool addDns) + { + this.Function = "UpdateTunIpv4"; + this.Payload = new ConfigPayload() + { + TunIPv4 = tunIPv4, + TunIPv4Mask = tunIPv4Mask, + AddDns = addDns + }; + } + public ConfigPayload Payload { get; set; } + } + } diff --git a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs index af87b68d..cc1f0695 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs @@ -390,6 +390,30 @@ namespace ZitiDesktopEdge.ServiceClient { return; } + async public Task UpdateConfigAsync(string tunIPv4, int tunIPv4Mask, bool addDns) { + SvcResponse resp = null; + try { + + ConfigUpdateFunction configPayload = new ConfigUpdateFunction(tunIPv4, tunIPv4Mask, addDns); + + await sendAsync(configPayload); + resp = await readAsync(ipcReader); + Logger.Debug("config update payload is sent to the ziti tunnel"); + } catch (Exception ex) { + //almost certainly a problem with the pipe - recreate the pipe... + //setupPipe(); + //throw; + Logger.Error(ex, "Unexpected error"); + CommunicationError(ex); + throw ex; + } + if (resp?.Code != 0) { + Logger.Warn("failed to update the config. {0} {1}", resp.Message, resp.Error); + throw new ServiceException("Failed to update the config", resp.Code, "Un expected error."); + } + return resp; + } + async public Task debugAsync() { try { await sendAsync(new ServiceFunction() { Function = "Debug" }); diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs index 6c15d9ab..7b16007f 100644 --- a/ZitiUpdateService/Properties/AssemblyInfo.cs +++ b/ZitiUpdateService/Properties/AssemblyInfo.cs @@ -30,6 +30,6 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.3.0")] -[assembly: AssemblyVersion("1.9.3.0")] -[assembly: AssemblyFileVersion("1.9.3.0")] +// [assembly: AssemblyVersion("1.9.4.0")] +[assembly: AssemblyVersion("1.9.4.0")] +[assembly: AssemblyFileVersion("1.9.4.0")] diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index 57fdf030..529ae040 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -214,7 +214,7 @@ namespace ZitiUpdateService { } return zipName; } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); + Logger.Error(ex, "Unexpected error in generating system files {0}", ex.Message); return null; } } @@ -233,7 +233,7 @@ namespace ZitiUpdateService { process.Start(); process.WaitForExit(); } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); + Logger.Error(ex, "Unexpected error in outputIpconfigInfo {0}", ex.Message); } } @@ -251,7 +251,7 @@ namespace ZitiUpdateService { process.Start(); process.WaitForExit(); } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); + Logger.Error(ex, "Unexpected error in outputSystemInfo {0}", ex.Message); } } @@ -269,7 +269,7 @@ namespace ZitiUpdateService { process.Start(); process.WaitForExit(); } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); + Logger.Error(ex, "Unexpected error in outputDnsCache {0}", ex.Message); } } @@ -283,7 +283,7 @@ namespace ZitiUpdateService { string responseBody = resp.Content.ReadAsStringAsync().Result; File.WriteAllText(extIpFile, responseBody); } catch (Exception ex) { - Logger.Error(ex, "Unexpected error {0}", ex.Message); + Logger.Error(ex, "Unexpected error in outputExternalIP {0}", ex.Message); } } diff --git a/release-notes.md b/release-notes.md index 67b66a3f..6394b297 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,16 +1,14 @@ -# Release 1.9.3 +# Release 1.9.4 ## What's New -* Page Service List -* Sort Service on Identity Details Page -* Consistent UX for MFA Screens +* none ## Other changes: -* Service to UI interaction is now with one bulk update of services instead of one event per service update -* Added a configuration option to control if DNS is applied to the TUN. Some users are having issues with NRPT-only working. For now we'll add a boolean that allows the user to control if DNS should be added to the TUN +* none ## Bugs fixed: -* none +* [#362](https://github.com/openziti/desktop-edge-win/issues/362) Update tun ip, mask and addDns flag +* [#364](https://github.com/openziti/desktop-edge-win/issues/364) Add the test nrpt policy function in the startup method ## Dependency Updates * none @@ -24,6 +22,7 @@ ## Other changes: * Service to UI interaction is now with one bulk update of services instead of one event per service update +* Added a configuration option to control if DNS is applied to the TUN. Some users are having issues with NRPT-only working. For now we'll add a boolean that allows the user to control if DNS should be added to the TUN ## Bugs fixed: * none diff --git a/service/windns/windns.go b/service/windns/windns.go index ae2897c0..23e63f74 100644 --- a/service/windns/windns.go +++ b/service/windns/windns.go @@ -238,3 +238,53 @@ ForEach ($ns in $toRemove){ log.Errorf("ERROR removing nrpt rules: %v", err) } } + +func IsNrptPoliciesEffective(dnsserver string) bool { + script := fmt.Sprintf(`Add-DnsClientNrptRule -Namespace ".ziti.test" -NameServers "%s" -Comment "Added by ziti-tunnel" -DisplayName "ziti-tunnel:.ziti.test" + Get-DnsClientNrptPolicy -Effective | Select-Object Namespace -Unique | Where-Object Namespace -Eq ".ziti.test"`, dnsserver) + log.Debugf("checking the nrpt policies with: %s", script) + + cmd := exec.Command("powershell", "-Command", script) + cmd.Stderr = os.Stdout + output := new(bytes.Buffer) + cmd.Stdout = output + + err := cmd.Run() + if err != nil { + log.Errorf("ERROR adding the test nrpt rules: %v", err) + return false + } + + policyFound := false + for { + l, err := output.ReadString('\n') + policyOut := strings.Trim(l, "\r\n") + if err != nil { + break + } + + if strings.Compare(policyOut, ".ziti.test") == 0 { + policyFound = true + log.Debug("The nrpt policies are effective in this client") + break + } + } + removeSingleNrtpRule(".ziti.test") + + return policyFound +} + +func removeSingleNrtpRule(nrptRule string) { + script := fmt.Sprintf(`Get-DnsClientNrptRule | where Namespace -eq "%s" | Remove-DnsClientNrptRule -Force -ErrorAction SilentlyContinue`, nrptRule) + log.Debugf("Removing the nrpt rule with: %s", script) + + cmd := exec.Command("powershell", "-Command", script) + cmd.Stderr = os.Stdout + output := new(bytes.Buffer) + cmd.Stdout = output + + err := cmd.Run() + if err != nil { + log.Errorf("ERROR removing the nrpt rules: %v", err) + } +} diff --git a/service/ziti-tunnel/cli/constants.go b/service/ziti-tunnel/cli/constants.go index 3c21a278..150d45af 100644 --- a/service/ziti-tunnel/cli/constants.go +++ b/service/ziti-tunnel/cli/constants.go @@ -42,6 +42,10 @@ var NOTIFY_IDENTITY_UI = dto.CommandMsg{ Function: "NotifyIdentityUI", } +var UPDATE_TUN_IPV4 = dto.CommandMsg{ + Function: "UpdateTunIpv4", +} + var monitorIpcPipe = `\\.\pipe\OpenZiti\ziti-monitor\ipc` var templateIdentity = `{{printf "%40s" "Name"}} | {{printf "%41s" "FingerPrint"}} | {{printf "%6s" "Active"}} | {{printf "%30s" "Config"}} | {{"Status"}} diff --git a/service/ziti-tunnel/cli/responseGenerator.go b/service/ziti-tunnel/cli/responseGenerator.go index f0143d26..d469dad7 100644 --- a/service/ziti-tunnel/cli/responseGenerator.go +++ b/service/ziti-tunnel/cli/responseGenerator.go @@ -57,11 +57,11 @@ func convertToServiceCli(svc dto.Service) dto.ServiceCli { } return dto.ServiceCli{ - Name: svc.Name, - Id: svc.Id, - Protocols: strings.Join(svc.Protocols, ","), - Ports: cliPorts, - Addresses: cliAddresses, + Name: svc.Name, + Id: svc.Id, + Protocols: strings.Join(svc.Protocols, ","), + Ports: cliPorts, + Addresses: cliAddresses, } } diff --git a/service/ziti-tunnel/cli/service.go b/service/ziti-tunnel/cli/service.go index ae695c88..bda4320d 100644 --- a/service/ziti-tunnel/cli/service.go +++ b/service/ziti-tunnel/cli/service.go @@ -1,9 +1,27 @@ package cli -import ( - "strings" +/* + * Copyright NetFoundry, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import ( "github.com/openziti/desktop-edge-win/service/ziti-tunnel/dto" + "net" + "strconv" + "strings" ) //GetIdentities is to fetch identities through cmdline @@ -53,3 +71,40 @@ func SetLogLevel(args []string, flags map[string]bool) { func GetFeedback(args []string, flags map[string]bool) { GetDataFromMonitorIpcPipe(&dto.FEEDBACK_REQUEST, args, flags) } + +func UpdateConfigIPSubnet(args []string, flags map[string]interface{}) { + CIDRstring := flags["CIDR"] + AddDns := flags["AddDns"] + log.Info("Updating the config file") + + updateTunIpv4Payload := make(map[string]interface{}) + if CIDRstring != "" { + ip, ipnet, err := net.ParseCIDR(CIDRstring.(string)) + if err != nil { + log.Error("Incorrect cidr %s", CIDRstring.(string)) + } + ipMask, _ := ipnet.Mask.Size() + + updateTunIpv4Payload["TunIPv4"] = ip + updateTunIpv4Payload["TunIPv4Mask"] = ipMask + } + if AddDns != "" { + addDnsBool, err := strconv.ParseBool(AddDns.(string)) + + if err != nil { + log.Errorf("Incorrect addDns %v", err) + return + } + updateTunIpv4Payload["AddDns"] = addDnsBool + } + + UPDATE_TUN_IPV4.Payload = updateTunIpv4Payload + log.Debugf("updateTunIpv4 Payload %v", UPDATE_TUN_IPV4) + + status := GetDataFromIpcPipe(&UPDATE_TUN_IPV4, nil, GetResponseObjectFromRTS, args, nil) + + if !status { + log.Infof("Config can be updated only if the tunnel is started") + } + +} diff --git a/service/ziti-tunnel/cmd/config.go b/service/ziti-tunnel/cmd/config.go new file mode 100644 index 00000000..81c2ee08 --- /dev/null +++ b/service/ziti-tunnel/cmd/config.go @@ -0,0 +1,41 @@ +package cmd + +/* + * Copyright NetFoundry, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import ( + "github.com/spf13/cobra" +) + +// configCmd represents the config command +var configCmd = &cobra.Command{ + Use: "config", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + checkHelp() + }, +} + +func init() { + rootCmd.AddCommand(configCmd) +} diff --git a/service/ziti-tunnel/cmd/update.go b/service/ziti-tunnel/cmd/update.go new file mode 100644 index 00000000..a72d9958 --- /dev/null +++ b/service/ziti-tunnel/cmd/update.go @@ -0,0 +1,50 @@ +package cmd + +/* + * Copyright NetFoundry, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import ( + "github.com/openziti/desktop-edge-win/service/ziti-tunnel/cli" + + "github.com/spf13/cobra" +) + +var CIDR string +var AddDns string + +// updateCmd represents the update command +var updateCmd = &cobra.Command{ + Use: "update", + Short: "Updates data into the ziti-tunnel", + Long: `update command should be used as a sub command to the main command + It helps to update the data into the ziti-tunnel. + eg: ziti-tunnel config update --CIDR 100.64.0.1/10 --AddDns=true`, + Run: func(cmd *cobra.Command, args []string) { + flags := map[string]interface{}{} + flags["CIDR"] = CIDR + flags["AddDns"] = AddDns + cli.UpdateConfigIPSubnet(args, flags) + }, +} + +func init() { + configCmd.AddCommand(updateCmd) + + updateCmd.Flags().StringVar(&CIDR, "CIDR", "", "Updates the cidr property in the config file") + updateCmd.Flags().StringVar(&AddDns, "AddDns", "", "Updates the Add Dns property in the config file") + +} diff --git a/service/ziti-tunnel/dto/clidtos.go b/service/ziti-tunnel/dto/clidtos.go index 6f7e840d..df93a954 100644 --- a/service/ziti-tunnel/dto/clidtos.go +++ b/service/ziti-tunnel/dto/clidtos.go @@ -27,11 +27,11 @@ type IdentityCli struct { } type ServiceCli struct { - Name string - Id string - Protocols string - Ports string - Addresses string + Name string + Id string + Protocols string + Ports string + Addresses string } type IdentityOnOffPayload struct { diff --git a/service/ziti-tunnel/main.go b/service/ziti-tunnel/main.go index c1c3f236..1b5a51db 100644 --- a/service/ziti-tunnel/main.go +++ b/service/ziti-tunnel/main.go @@ -111,6 +111,8 @@ func main() { commandline.Execute() case "feedback": commandline.Execute() + case "config": + commandline.Execute() default: usage(fmt.Sprintf("invalid command %s", cmd)) } @@ -126,7 +128,7 @@ func usage(errmsg string) { "%s\n\n"+ "usage: %s \n"+ " where is one of\n"+ - " install, remove, debug, start, stop, pause, continue, list, identity, loglevel, feedback or version.\n", + " install, remove, debug, start, stop, pause, continue, list, identity, loglevel, feedback, config or version.\n", errmsg, os.Args[0]) os.Exit(2) } diff --git a/service/ziti-tunnel/service/ipc.go b/service/ziti-tunnel/service/ipc.go index 4bd5cf49..f06e14ea 100644 --- a/service/ziti-tunnel/service/ipc.go +++ b/service/ziti-tunnel/service/ipc.go @@ -41,6 +41,7 @@ import ( "net" "os" "os/signal" + "strconv" "strings" "sync" "time" @@ -498,6 +499,21 @@ func serveIpc(conn net.Conn) { //save the state rts.SaveState() + case "UpdateTunIpv4": + var tunIPv4 string + var tunIPv4Mask int + var addDns string + if cmd.Payload["TunIPv4"] != nil { + tunIPv4 = cmd.Payload["TunIPv4"].(string) + } + if cmd.Payload["TunIPv4Mask"] != nil { + var tunIpv4Mask = cmd.Payload["TunIPv4Mask"].(float64) + tunIPv4Mask = int(tunIpv4Mask) + } + if cmd.Payload["AddDns"] != nil { + addDns = strconv.FormatBool(cmd.Payload["AddDns"].(bool)) + } + updateTunIpv4(enc, tunIPv4, tunIPv4Mask, addDns) case "NotifyLogLevelUIAndUpdateService": sendLogLevelAndNotify(enc, cmd.Payload["Level"].(string)) case "NotifyIdentityUI": @@ -622,6 +638,17 @@ func setLogLevel(out *json.Encoder, level string) { respond(out, dto.Response{Message: "log level set", Code: SUCCESS, Error: "", Payload: nil}) } +func updateTunIpv4(out *json.Encoder, ip string, ipMask int, addDns string) { + + err := UpdateRuntimeStateIpv4(ip, ipMask, addDns) + if err != nil { + respondWithError(out, "Could not set Tun ip and mask", UNKNOWN_ERROR, err) + return + } + + respond(out, dto.Response{Message: "TunIPv4 and mask is set, Manual Restart is required", Code: SUCCESS, Error: "", Payload: ""}) +} + func serveLogs(conn net.Conn) { log.Debug("accepted a logs connection, writing logs to pipe") w := bufio.NewWriter(conn) diff --git a/service/ziti-tunnel/service/state.go b/service/ziti-tunnel/service/state.go index cb5b06a3..7d667cfd 100644 --- a/service/ziti-tunnel/service/state.go +++ b/service/ziti-tunnel/service/state.go @@ -20,24 +20,26 @@ package service import ( "bufio" "encoding/json" + "errors" "fmt" + "github.com/openziti/desktop-edge-win/service/cziti" + "github.com/openziti/desktop-edge-win/service/windns" + "github.com/openziti/desktop-edge-win/service/ziti-tunnel/config" + "github.com/openziti/desktop-edge-win/service/ziti-tunnel/constants" + "github.com/openziti/desktop-edge-win/service/ziti-tunnel/dto" + idcfg "github.com/openziti/sdk-golang/ziti/config" "golang.org/x/sys/windows" + "golang.org/x/sys/windows/registry" + "golang.zx2c4.com/wireguard/tun" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "io" "io/ioutil" "net" "os" "path" + "strconv" "strings" "time" - - "github.com/openziti/desktop-edge-win/service/cziti" - "github.com/openziti/desktop-edge-win/service/ziti-tunnel/config" - "github.com/openziti/desktop-edge-win/service/ziti-tunnel/constants" - "github.com/openziti/desktop-edge-win/service/ziti-tunnel/dto" - idcfg "github.com/openziti/sdk-golang/ziti/config" - "golang.org/x/sys/windows/registry" - "golang.zx2c4.com/wireguard/tun" - "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" ) type RuntimeState struct { @@ -221,7 +223,14 @@ func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int, applyDns bool) (net. } log.Info("routing applied") - if applyDns { + zitiPoliciesEffective := windns.IsNrptPoliciesEffective(ipv4) + if applyDns || !zitiPoliciesEffective { + if applyDns { + log.Infof("DNS is applied to the TUN interface, because apply Dns flag in the config file is %t ", applyDns) + } + if !applyDns && !zitiPoliciesEffective { + log.Infof("DNS is applied to the TUN interface, because Ziti policies test result in this client is %t", zitiPoliciesEffective) + } //for windows 10+, could 'domains' be able to replace NRPT? dunno - didn't test it luid.SetDNS(windows.AF_INET, []net.IP{ip}, nil) } @@ -401,6 +410,35 @@ func (t *RuntimeState) UpdateIpv4(ipv4 string) { rts.SaveState() } +func UpdateRuntimeStateIpv4(ip string, ipv4Mask int, addDns string) error { + + log.Infof("updating configuration ip: %s, mask: %d, dns: %t", ip, ipv4Mask, addDns) + + if ipv4Mask < constants.Ipv4MaxMask || ipv4Mask > constants.Ipv4MinMask { + return errors.New(fmt.Sprintf("ipv4Mask should be between %d and %d", constants.Ipv4MaxMask, constants.Ipv4MinMask)) + } + + if addDns != "" { + addDnsBool, err := strconv.ParseBool(addDns) + + if err != nil { + return errors.New(fmt.Sprintf("Incorrect addDns %v", err)) + } + + rts.state.AddDns = addDnsBool + } + + // if ip is not empty, then we set both ip and mask + if ip != "" { + rts.state.TunIpv4 = ip + rts.state.TunIpv4Mask = ipv4Mask + } + + rts.SaveState() + + return nil +} + // uses the registry to determine if IPv6 is enabled or disabled on this machine. If it is disabled an IPv6 DNS entry // will end up causing a fatal error on startup of the service. For this registry key and values see the MS documentation // at https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-ipv6-in-windows diff --git a/service/ziti-tunnel/version.go b/service/ziti-tunnel/version.go index 6f114ee5..7e992ac7 100644 --- a/service/ziti-tunnel/version.go +++ b/service/ziti-tunnel/version.go @@ -20,9 +20,9 @@ package main const ( - Version = "1.9.3" - Revision = "a02a50cf6437" - Branch = "main" + Version = "1.9.4" + Revision = "2db5f6a8362d" + Branch = "release-next" BuildUser = "" - BuildDate = "2021-04-12 17:35:14" + BuildDate = "2021-04-21 18:46:25" ) diff --git a/version b/version index 77fee73a..d615fd0c 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.9.3 +1.9.4