Skip to content
This repository has been archived by the owner on Dec 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request #100 from StrangeRanger/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
StrangeRanger authored Mar 17, 2024
2 parents 89bf3b1 + ed2b485 commit 7c7553d
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static async Task<ObservableCollection<Command>> GetADCommands()
if (psOutput.HadErrors)
{
string errorMessage = "Internal Error: An error occurred while retrieving the Active Directory commands: " +
$"({string.Join(" ", psOutput.StdErr)})";
$"{string.Join(" ", psOutput.StdErr)}";
MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
throw new InvalidPowerShellStateException(errorMessage);
}
Expand Down
53 changes: 53 additions & 0 deletions ActiveDirectoryQuerier/ActiveDirectoryInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Management.Automation.Runspaces;
using ActiveDirectoryQuerier.PowerShell;

namespace ActiveDirectoryQuerier;

public class ActiveDirectoryInfo
{
private readonly PSExecutor _psExecutor = new();

public Dictionary<string, Func<Task<PSOutput>>> AvailableOptions { get; } = new();

public ActiveDirectoryInfo()
{
AvailableOptions.Add("Get user on domain", GetADUsers);
AvailableOptions.Add("Get computers on domain", GetADComputers);
AvailableOptions.Add("Get IPv4 of each system on domain", GetADIPv4Addresses);
AvailableOptions.Add("Get IPv6 of each system on domain", GetADIPv6Addresses);
}

// ReSharper disable once InconsistentNaming
private async Task<PSOutput> GetADUsers()
{
Command psCommand = new("Get-ADUser");
psCommand.Parameters.Add("Filter", "*");
return await _psExecutor.ExecuteAsync(psCommand);
}

// ReSharper disable once InconsistentNaming
private async Task<PSOutput> GetADComputers()
{
Command psCommand = new("Get-ADComputer");
psCommand.Parameters.Add("Filter", "*");
return await _psExecutor.ExecuteAsync(psCommand);
}

// ReSharper disable once InconsistentNaming
private async Task<PSOutput> GetADIPv4Addresses()
{
Command psCommand = new("Get-ADComputer");
psCommand.Parameters.Add("Filter", "*");
psCommand.Parameters.Add("Properties", "IPv4Address");
return await _psExecutor.ExecuteAsync(psCommand);
}

// ReSharper disable once InconsistentNaming
private async Task<PSOutput> GetADIPv6Addresses()
{
Command psCommand = new("Get-ADComputer");
psCommand.Parameters.Add("Filter", "*");
psCommand.Parameters.Add("Properties", "IPv6Address");
return await _psExecutor.ExecuteAsync(psCommand);
}
}
4 changes: 2 additions & 2 deletions ActiveDirectoryQuerier/ActiveDirectoryQuerier.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<!-- Project Info. -->
<Title>Active Directory Querier</Title>
<Version>0.6.0</Version>
<Version>0.7.0</Version>
<Authors>Hunter T., Pieter, Joseph</Authors>
<RepositoryUrl>https://github.com/StrangeRanger/Active-Directory-Querier</RepositoryUrl>

Expand All @@ -22,7 +22,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.18" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.11" />
</ItemGroup>

</Project>
7 changes: 3 additions & 4 deletions ActiveDirectoryQuerier/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,14 @@
<RowDefinition MinHeight="100" />
</Grid.RowDefinitions>
<Grid Margin="0,0,0,49" Grid.Row="0">
<!-- TODO: Pieter: Bind button to another property inside the MainWindowViewModel -->
<Button Content="Execute" HorizontalAlignment="Left" Margin="368,43,0,0"
VerticalAlignment="Top" Height="23" Width="95" />
VerticalAlignment="Top" Height="23" Width="95"
Command="{Binding ExecuteQueryAsyncFromActiveDirectoryInfoRelay}"/>
<ComboBox HorizontalAlignment="Left" Margin="31,44,0,0" VerticalAlignment="Top"
Width="207" Height="26"
SelectedItem="{Binding SelectedCommandFromComboBoxInActiveDirectoryInfo}"
ItemsSource="{Binding AvailableOptionsFromComboBoxInActiveDirectoryInfo}" />
ItemsSource="{Binding AvailableOptionsFromComboBoxInActiveDirectoryInfo.AvailableOptions.Keys }" />
</Grid>
<!-- TODO: Pieter: Bind this to a property in the MainWindowViewModel -->
<TextBox Grid.Row="1" Text="{Binding ConsoleOutputInActiveDirectoryInfo.ConsoleOutput}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="10" IsReadOnly="True" TextWrapping="Wrap"
Expand Down
148 changes: 91 additions & 57 deletions ActiveDirectoryQuerier/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using ActiveDirectoryQuerier.PowerShell;
using ActiveDirectoryQuerier.Queries;
using ActiveDirectoryQuerier.ViewModels;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Win32;

namespace ActiveDirectoryQuerier;
Expand All @@ -30,7 +31,7 @@ public sealed class MainWindowViewModel : INotifyPropertyChanged
private AppConsole _consoleOutputInQueryBuilder;
private AppConsole _consoleOutputInActiveDirectoryInfo;
private Command? _selectedCommandFromComboBoxInQueryBuilder;
private Command? _selectedCommandFromComboBoxInActiveDirectoryInfo;
private string? _selectedCommandFromComboBoxInActiveDirectoryInfo;
private ObservableCollection<Button>? _buttons; // TODO: Rename to be more descriptive.

// [[ Other fields ]] ----------------------------------------------------------- //
Expand All @@ -39,6 +40,7 @@ public sealed class MainWindowViewModel : INotifyPropertyChanged
private readonly CustomQueries _customQuery;
private readonly PSExecutor _psExecutor;
private Query? _isEditing;
private readonly ActiveDirectoryInfo _activeDirectoryInfo = new();

// [ Properties ] --------------------------------------------------------------- //
// [[ Properties for backing fields ]] ------------------------------------------ //
Expand Down Expand Up @@ -104,13 +106,13 @@ public Command? SelectedCommandFromComboBoxInQueryBuilder
// No need to load parameters if the command is null.
if (value is not null)
{
// TODO: Figure out how to resolve the warning about the async method not being awaited!!!
// TODO: Figure out how to resolve the warning about the async method not being awaited...
LoadCommandParametersAsync(value);

Check warning on line 110 in ActiveDirectoryQuerier/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build (x64)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 110 in ActiveDirectoryQuerier/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build (x64)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 110 in ActiveDirectoryQuerier/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build (x64)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 110 in ActiveDirectoryQuerier/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build (x64)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
}
}
}

public Command? SelectedCommandFromComboBoxInActiveDirectoryInfo
public string? SelectedCommandFromComboBoxInActiveDirectoryInfo
{
get => _selectedCommandFromComboBoxInActiveDirectoryInfo;
set {
Expand All @@ -122,9 +124,7 @@ public Command? SelectedCommandFromComboBoxInActiveDirectoryInfo
}
}

public List<string> AvailableOptionsFromComboBoxInActiveDirectoryInfo {
get;
} = new() { "Get user on domain", "Get computers on domain", "Get IP of each system on domain" };
public ActiveDirectoryInfo AvailableOptionsFromComboBoxInActiveDirectoryInfo { get; } = new();

public ObservableCollection<Button> QueryButtonStackPanel => _buttons ??= new ObservableCollection<Button>();

Expand All @@ -151,7 +151,7 @@ public List<string> AvailableOptionsFromComboBoxInActiveDirectoryInfo {
public ICommand SaveQueryRelay { get; }
public ICommand ClearQueryBuilderRelay { get; }
public ICommand ExecuteQueryFromQueryBuilderRelay { get; }
public ICommand ExecuteQueryFromActiveDirectoryInfoRelay { get; } // TODO: Pieter use this for execution button
public ICommand ExecuteQueryAsyncFromActiveDirectoryInfoRelay { get; }
public ICommand AddCommandComboBoxRelay { get; }
public ICommand AddCommandParameterComboBoxRelay { get; }
public ICommand RemoveCommandParameterComboBoxRelay { get; }
Expand All @@ -161,18 +161,8 @@ public List<string> AvailableOptionsFromComboBoxInActiveDirectoryInfo {
public ICommand ClearConsoleOutputInQueryBuilderRelay { get; }
public ICommand ImportQueryFileRelay { get; }
public ICommand CreateNewQueryFileRelay { get; }
public ICommand ClearConsoleOutputInActiveDirectoryInfoRelay { get; } // TODO: Impliment functionality.....

/* TODO: for Pieter
*
* Create a property that contains the ComboBox dropdown options/text.
*
* Create a property that will contain the selected item.
*
* Create a property to act as the relay to the execution button.
*/

// NEW CODE
public ICommand ClearConsoleOutputInActiveDirectoryInfoRelay { get; }

// [ Constructor ] ------------------------------------------------------------- //

public MainWindowViewModel()
Expand All @@ -192,12 +182,10 @@ public MainWindowViewModel()
OutputToCsvFileRelay = new RelayCommand(OutputExecutionResultsToCsvFileAsync);
OutputToTextFileRelay = new RelayCommand(OutputExecutionResultsToTextFileAsync);
ExportConsoleOutputRelay = new RelayCommand(ExportConsoleOutputToFile);
// TODO: Figure out how resolve the warning about the async method not being awaited.
ExecuteQueryFromQueryBuilderRelay = new RelayCommand(
_ => ExecuteQuery(_consoleOutputInQueryBuilder));
// TODO: Figure out how resolve the warning about the async method not being awaited.
ExecuteQueryFromActiveDirectoryInfoRelay = new RelayCommand(
_ => ExecuteQuery(_consoleOutputInActiveDirectoryInfo));
_ => ExecuteQueryAsync(_consoleOutputInQueryBuilder));
ExecuteQueryAsyncFromActiveDirectoryInfoRelay =
new RelayCommand(ExecuteQueryAsyncFromComboBoxInActiveDirectoryInfo);
ImportQueryFileRelay = new RelayCommand(ImportQueryFile);
CreateNewQueryFileRelay = new RelayCommand(CreateNewQueryFile);
AddCommandParameterComboBoxRelay = new RelayCommand(AddParameterComboBoxInQueryBuilder);
Expand All @@ -212,30 +200,49 @@ public MainWindowViewModel()
ClearConsoleOutputInActiveDirectoryInfoRelay = new RelayCommand(
_ => ClearConsoleOutput(_consoleOutputInActiveDirectoryInfo));
ClearQueryBuilderRelay = new RelayCommand(ClearQueryBuilder);

/* TODO: For Pieter
* Connect the relay property to for the execute button to you method that performs the execution.
*/

// TODO: Figure out how resolve the warning about the async method not being awaited.

InitializeActiveDirectoryCommandsAsync();
LoadSavedQueriesFromFile(); // Calls method to deserialize and load buttons.
}

// [ Methods ] ----------------------------------------------------------------- //

/* TODO: Info for Pieter to get started
* Create a class (outside of this one) that will contain three methods, all of each will perform one specific
* action, such as getting the users on the domain, getting computers on the domain, and getting the IP of each
* system on the domain.
* - The methods in this class should use the powershell executor to execute the specific command.
* - Make method async, and utilize the async execute methods in the powershell executor.
*
* public async Task<the return type> MethodName() { } // don't forget to use await when dealing with async
* methods.
*
* In this class, create a method or two, that will be used to execute the specific selected action.
*/
private async void ExecuteQueryAsyncFromComboBoxInActiveDirectoryInfo(object _)
{
if (SelectedCommandFromComboBoxInActiveDirectoryInfo is null)
{
Trace.WriteLine("No command selected.");
MessageBox.Show("You must first select an option to execute.",
"Warning",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}

string selectedOption = SelectedCommandFromComboBoxInActiveDirectoryInfo;
if (_activeDirectoryInfo.AvailableOptions.TryGetValue(selectedOption, out var method))
{
PSOutput result = await method.Invoke();

if (result.HadErrors)
{
ConsoleOutputInActiveDirectoryInfo.Append(result.StdErr);
}
else
{
ConsoleOutputInActiveDirectoryInfo.Append(result.StdOut);
}
}
// This is more of an internal error catch, as even through this command shouldn't fail, it's better safe than
// sorry.
else
{
string errorMessage =
"Internal Error: The selected option was not found in the dictionary: " + $"{selectedOption}";
MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
throw new KeyNotFoundException("The selected option was not found in the dictionary.");
}
}

private void ClearConsoleOutput(AppConsole appConsole)
{
Expand Down Expand Up @@ -303,7 +310,9 @@ private void EditQueryFromQueryStackPanel(object queryButton)

private async void ExecuteQueryFromQueryStackPanel(object queryButton)
{
if (queryButton is not Button currentButton)
var currentButton = queryButton as Button;

if (currentButton is null)
{
Trace.WriteLine("No button selected.");
MessageBox.Show("To execute a query, you must first select a query.",
Expand All @@ -312,6 +321,7 @@ private async void ExecuteQueryFromQueryStackPanel(object queryButton)
MessageBoxImage.Warning);
return;
}


var buttonQuery = (Query)currentButton.Tag;
await ExecuteQueryCoreAsync(ConsoleOutputInQueryBuilder, buttonQuery.Command);
Expand Down Expand Up @@ -395,19 +405,21 @@ private void ImportQueryFile(object _)
}
}

// TODO: Possibly change Task to void?
private async Task ExecuteQuery(AppConsole appConsole, Command? command = null)
// It's okay to suppress this warning because this method is called within the constructor. There is more than enough
// time for the method to complete before the user interacts with the GUI.
#pragma warning disable S3168
private async void ExecuteQueryAsync(AppConsole appConsole, Command? command = null)
{
await ExecuteQueryCoreAsync(appConsole, command);
}

// TODO: Possibly change Task to void?
private async Task InitializeActiveDirectoryCommandsAsync()
private async void InitializeActiveDirectoryCommandsAsync()
{
ObservableCollection<Command> list = await ADCommandsFetcher.GetADCommands();
ADCommands = new ObservableCollection<Command>(list);
OnPropertyChanged(nameof(ADCommands));
}
#pragma warning restore S3168

private async Task LoadCommandParametersAsync(Command? selectedCommand)
{
Expand Down Expand Up @@ -491,7 +503,20 @@ private void AddCommandComboBoxInQueryBuilder(object _)

private async void OutputExecutionResultsToTextFileAsync(object _)
{
await ExecuteQueryCoreAsync(ConsoleOutputInQueryBuilder);

if (_.GetType() == typeof(Button))
{
var currentButton = _ as Button;
Query buttonQuery;

buttonQuery = (Query)currentButton!.Tag;
await ExecuteQueryCoreAsync(ConsoleOutputInQueryBuilder, buttonQuery.Command);

}
else
{
await ExecuteQueryCoreAsync(ConsoleOutputInQueryBuilder);
}

// Filepath
// Write the text to a file & prompt user for the location
Expand Down Expand Up @@ -519,7 +544,20 @@ private async void OutputExecutionResultsToTextFileAsync(object _)
/// <param name="_">Represents the object that the command is bound to</param>
private async void OutputExecutionResultsToCsvFileAsync(object _)
{
await ExecuteQueryCoreAsync(ConsoleOutputInQueryBuilder);

if (_.GetType() == typeof(Button))
{
var currentButton = _ as Button;
Query buttonQuery;

buttonQuery = (Query)currentButton!.Tag;
await ExecuteQueryCoreAsync(ConsoleOutputInQueryBuilder, buttonQuery.Command);

}
else
{
await ExecuteQueryCoreAsync(ConsoleOutputInQueryBuilder);
}

var csv = new StringBuilder();
string[] output = ConsoleOutputInQueryBuilder.ConsoleOutput.Split(' ', '\n');
Expand Down Expand Up @@ -570,10 +608,6 @@ private void ExportConsoleOutputToFile(object _)
/// <summary>
/// This method is for getting the currently selected command at anytime
/// </summary>
/// <note>
/// TODO: !Still in the works!
/// TODO: Does this method do the same thing an another method?
/// </note>
private void UpdateSelectedCommand()
{
if (SelectedCommandFromComboBoxInQueryBuilder is null)
Expand Down Expand Up @@ -642,7 +676,7 @@ private void GetCurrentQuery()
// TODO: Possibly provide more comprehensive error handling.
catch (Exception ex)
{
Trace.WriteLine(ex);
MessageBox.Show(ex.Message);
}
}

Expand Down Expand Up @@ -770,9 +804,9 @@ private Button CreateQueryButtonInStackPanel(Query? query = null)
MenuItem menuItem1 =
new() { Header = "Execute", Command = ExecuteQueryFromQueryStackPanelRelay, CommandParameter = newButton };

MenuItem outputToCsv = new() { Header = "Output to CSV", Command = OutputToCsvFileRelay };
MenuItem outputToText = new() { Header = "Output to Text", Command = OutputToTextFileRelay };
MenuItem outputToConsole = new() { Header = "Execute to Console", Command = ExecuteQueryFromQueryBuilderRelay };
MenuItem outputToCsv = new() { Header = "Output to CSV", Command = OutputToCsvFileRelay, CommandParameter = newButton };
MenuItem outputToText = new() { Header = "Output to Text", Command = OutputToTextFileRelay, CommandParameter = newButton };
MenuItem outputToConsole = new() { Header = "Execute to Console", Command = ExecuteQueryFromQueryStackPanelRelay, CommandParameter = newButton };

menuItem1.Items.Add(outputToCsv);
menuItem1.Items.Add(outputToText);
Expand Down

0 comments on commit 7c7553d

Please sign in to comment.