diff --git a/module/PSADTree.psd1 b/module/PSADTree.psd1 index c8d5135..998f53e 100644 --- a/module/PSADTree.psd1 +++ b/module/PSADTree.psd1 @@ -16,7 +16,7 @@ } # Version number of this module. - ModuleVersion = '1.1.3' + ModuleVersion = '1.1.4' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/src/PSADTree/Commands/GetADTreeGroupMemberCommand.cs b/src/PSADTree/Commands/GetADTreeGroupMemberCommand.cs index ab2ffca..181a617 100644 --- a/src/PSADTree/Commands/GetADTreeGroupMemberCommand.cs +++ b/src/PSADTree/Commands/GetADTreeGroupMemberCommand.cs @@ -2,7 +2,7 @@ using System.DirectoryServices.AccountManagement; using System.Management.Automation; -namespace PSADTree; +namespace PSADTree.Commands; [Cmdlet( VerbsCommon.Get, "ADTreeGroupMember", @@ -43,11 +43,11 @@ protected override void ProcessRecord() } catch (MultipleMatchesException e) { - WriteError(ErrorHelper.AmbiguousIdentity(Identity, e)); + WriteError(e.AmbiguousIdentity(Identity)); } catch (Exception e) { - WriteError(ErrorHelper.Unspecified(Identity, e)); + WriteError(e.Unspecified(Identity)); } } @@ -111,7 +111,7 @@ private TreeObjectBase[] Traverse( } catch (Exception e) { - WriteError(ErrorHelper.EnumerationFailure(current, e)); + WriteError(e.EnumerationFailure(current)); } } @@ -124,7 +124,7 @@ private void EnumerateMembers( string source, int depth) { - foreach (Principal member in searchResult) + foreach (Principal member in searchResult.GetSortedEnumerable(_comparer)) { IDisposable? disposable = null; try @@ -135,10 +135,9 @@ private void EnumerateMembers( continue; } - if (member is not GroupPrincipal) + if (member.StructuralObjectClass != "group") { disposable = member; - if (Group.IsPresent) { continue; @@ -199,7 +198,7 @@ TreeObjectBase HandleGroup( return treeGroup; } - treeGroup = new(source, parent, group, depth); + treeGroup = new TreeGroup(source, parent, group, depth); Push(group, treeGroup); return treeGroup; } diff --git a/src/PSADTree/Commands/GetADTreePrincipalGroupMembershipCommand.cs b/src/PSADTree/Commands/GetADTreePrincipalGroupMembershipCommand.cs index 799e703..25d83dd 100644 --- a/src/PSADTree/Commands/GetADTreePrincipalGroupMembershipCommand.cs +++ b/src/PSADTree/Commands/GetADTreePrincipalGroupMembershipCommand.cs @@ -1,9 +1,8 @@ using System; using System.DirectoryServices.AccountManagement; -using System.Linq; using System.Management.Automation; -namespace PSADTree; +namespace PSADTree.Commands; [Cmdlet( VerbsCommon.Get, "ADTreePrincipalGroupMembership", @@ -32,12 +31,12 @@ protected override void ProcessRecord() } catch (MultipleMatchesException e) { - WriteError(ErrorHelper.AmbiguousIdentity(Identity, e)); + WriteError(e.AmbiguousIdentity(Identity)); return; } catch (Exception e) { - WriteError(ErrorHelper.Unspecified(Identity, e)); + WriteError(e.Unspecified(Identity)); return; } @@ -71,10 +70,11 @@ protected override void ProcessRecord() try { using PrincipalSearchResult search = principal.GetGroups(); - foreach (GroupPrincipal parent in search.Cast()) + foreach (Principal parent in search) { - TreeGroup treeGroup = new(source, null, parent, 1); - Push(parent, treeGroup); + GroupPrincipal groupPrincipal = (GroupPrincipal)parent; + TreeGroup treeGroup = new(source, null, groupPrincipal, 1); + Push(groupPrincipal, treeGroup); } } catch (Exception e) when (e is PipelineStoppedException or FlowControlException) @@ -83,7 +83,7 @@ protected override void ProcessRecord() } catch (Exception e) { - WriteError(ErrorHelper.EnumerationFailure(null, e)); + WriteError(e.EnumerationFailure(null)); } finally { @@ -149,7 +149,7 @@ private TreeObjectBase[] Traverse(string source) } catch (Exception e) { - WriteError(ErrorHelper.EnumerationFailure(current, e)); + WriteError(e.EnumerationFailure(current)); } } @@ -162,9 +162,9 @@ private void EnumerateMembership( string source, int depth) { - foreach (GroupPrincipal group in searchResult.Cast()) + foreach (Principal group in searchResult) { - TreeGroup treeGroup = ProcessGroup(group); + TreeGroup treeGroup = ProcessGroup((GroupPrincipal)group); if (ShowAll.IsPresent) { parent.AddChild(treeGroup); @@ -192,8 +192,9 @@ private void EnumerateMembership(TreeGroup parent, int depth) return; } - foreach (TreeGroup group in parent.Childs.Cast()) + foreach (TreeObjectBase child in parent.Childs) { + TreeGroup group = (TreeGroup)child; Push(null, (TreeGroup)group.Clone(parent, depth)); } } diff --git a/src/PSADTree/ErrorHelper.cs b/src/PSADTree/ErrorHelper.cs index dddb695..10e0cb3 100644 --- a/src/PSADTree/ErrorHelper.cs +++ b/src/PSADTree/ErrorHelper.cs @@ -13,14 +13,15 @@ internal static ErrorRecord IdentityNotFound(string? identity) => ErrorCategory.ObjectNotFound, identity); - internal static ErrorRecord AmbiguousIdentity(string? identity, Exception exception) => + internal static ErrorRecord AmbiguousIdentity(this Exception exception, string? identity) => new(exception, "AmbiguousIdentity", ErrorCategory.InvalidResult, identity); - internal static ErrorRecord Unspecified(string? identity, Exception exception) => + internal static ErrorRecord Unspecified(this Exception exception, string? identity) => new(exception, "Unspecified", ErrorCategory.NotSpecified, identity); - internal static ErrorRecord EnumerationFailure( - GroupPrincipal? groupPrincipal, - Exception exception) => + internal static ErrorRecord EnumerationFailure(this Exception exception, GroupPrincipal? groupPrincipal) => new(exception, "EnumerationFailure", ErrorCategory.NotSpecified, groupPrincipal); + + internal static ErrorRecord SetPrincipalContext(this Exception exception) => + new(exception, "SetPrincipalContext", ErrorCategory.ConnectionError, null); } diff --git a/src/PSADTree/PSADTree.csproj b/src/PSADTree/PSADTree.csproj index 55fd80f..4866383 100644 --- a/src/PSADTree/PSADTree.csproj +++ b/src/PSADTree/PSADTree.csproj @@ -13,16 +13,19 @@ $(DefineConstants);CORE + + + + + + + - - - - - - diff --git a/src/PSADTree/PSADTreeCmdletBase.cs b/src/PSADTree/PSADTreeCmdletBase.cs index 46c5416..7ae8766 100644 --- a/src/PSADTree/PSADTreeCmdletBase.cs +++ b/src/PSADTree/PSADTreeCmdletBase.cs @@ -21,6 +21,8 @@ public abstract class PSADTreeCmdletBase : PSCmdlet, IDisposable internal readonly TreeIndex _index = new(); + internal PSADTreeComparer _comparer = new(); + [Parameter( Position = 0, Mandatory = true, @@ -54,10 +56,9 @@ protected override void BeginProcessing() _context = new PrincipalContext(ContextType.Domain, Server); } - catch (Exception e) + catch (Exception exception) { - ThrowTerminatingError(new ErrorRecord( - e, "SetPrincipalContext", ErrorCategory.ConnectionError, null)); + ThrowTerminatingError(exception.SetPrincipalContext()); } } diff --git a/src/PSADTree/PSADTreeComparer.cs b/src/PSADTree/PSADTreeComparer.cs new file mode 100644 index 0000000..29ace4e --- /dev/null +++ b/src/PSADTree/PSADTreeComparer.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.DirectoryServices.AccountManagement; + +namespace PSADTree; + +#pragma warning disable CS8767 +internal sealed class PSADTreeComparer : IComparer +{ + public int Compare(Principal lhs, Principal rhs) => + lhs.StructuralObjectClass == "group" && rhs.StructuralObjectClass == "group" + ? rhs.SamAccountName.CompareTo(lhs.SamAccountName) // Groups in descending order + : lhs.SamAccountName.CompareTo(rhs.SamAccountName); // Other in ascending order +} diff --git a/src/PSADTree/TreeCache.cs b/src/PSADTree/TreeCache.cs index f045052..e7209da 100644 --- a/src/PSADTree/TreeCache.cs +++ b/src/PSADTree/TreeCache.cs @@ -10,7 +10,7 @@ internal sealed class TreeCache internal TreeGroup this[string distinguishedName] => _cache[distinguishedName]; - internal TreeCache() => _cache = new(); + internal TreeCache() => _cache = []; internal void Add(TreeGroup treeGroup) => _cache.Add(treeGroup.DistinguishedName, treeGroup); diff --git a/src/PSADTree/TreeExtensions.cs b/src/PSADTree/TreeExtensions.cs index d489690..551edbc 100644 --- a/src/PSADTree/TreeExtensions.cs +++ b/src/PSADTree/TreeExtensions.cs @@ -1,3 +1,5 @@ +using System.DirectoryServices.AccountManagement; +using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -58,6 +60,13 @@ internal static TreeObjectBase[] ConvertToTree( return inputObject; } + internal static IOrderedEnumerable GetSortedEnumerable( + this PrincipalSearchResult search, PSADTreeComparer comparer) => + search + .OrderBy(static e => e.StructuralObjectClass == "group") + .ThenBy(static e => e, comparer); + + private static void UpdateCorner(int index, TreeObjectBase current) { if (current.Hierarchy[index] == '└') diff --git a/src/PSADTree/TreeGroup.cs b/src/PSADTree/TreeGroup.cs index 54696c6..32a34e0 100644 --- a/src/PSADTree/TreeGroup.cs +++ b/src/PSADTree/TreeGroup.cs @@ -1,6 +1,8 @@ +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.DirectoryServices.AccountManagement; +using System.Text; namespace PSADTree; @@ -14,9 +16,11 @@ public sealed class TreeGroup : TreeObjectBase private const string _vtReset = "\x1B[0m"; + private static readonly StringBuilder s_sb = new(); + private List? _childs; - public ReadOnlyCollection Childs => new(_childs ??= new()); + public ReadOnlyCollection Childs => new(_childs ??= []); public bool IsCircular { get; private set; } @@ -46,23 +50,22 @@ internal TreeGroup( internal void SetCircularNested() { IsCircular = true; - Hierarchy = string.Concat( - Hierarchy.Insert( - Hierarchy.IndexOf("─ ") + 2, - _vtBrightRed), - _isCircular, - _vtReset); + Hierarchy = s_sb + .Append(Hierarchy.Insert(Hierarchy.IndexOf("─ ") + 2, _vtBrightRed)) + .Append(_isCircular) + .Append(_vtReset) + .ToString(); + + s_sb.Clear(); } - internal void SetProcessed() => - Hierarchy = string.Concat(Hierarchy, _isProcessed); + internal void SetProcessed() => Hierarchy = string.Concat(Hierarchy, _isProcessed); - internal void Hook(TreeCache cache) => - _childs ??= cache[DistinguishedName]._childs; + internal void Hook(TreeCache cache) => _childs ??= cache[DistinguishedName]._childs; internal void AddChild(TreeObjectBase child) { - _childs ??= new(); + _childs ??= []; _childs.Add(child); } diff --git a/src/PSADTree/TreeIndex.cs b/src/PSADTree/TreeIndex.cs index 8df3a0c..10c66d9 100644 --- a/src/PSADTree/TreeIndex.cs +++ b/src/PSADTree/TreeIndex.cs @@ -10,27 +10,24 @@ internal sealed class TreeIndex internal TreeIndex() { - _principals = new(); - _output = new(); + _principals = []; + _output = []; } - internal void AddPrincipal(TreeObjectBase principal) => - _principals.Add(principal); + internal void AddPrincipal(TreeObjectBase principal) => _principals.Add(principal); - internal void Add(TreeObjectBase principal) => - _output.Add(principal); + internal void Add(TreeObjectBase principal) => _output.Add(principal); internal void TryAddPrincipals() { if (_principals.Count > 0) { - _output.AddRange(_principals.ToArray()); + _output.AddRange([.. _principals]); _principals.Clear(); } } - internal TreeObjectBase[] GetTree() => - _output.ToArray().ConvertToTree(); + internal TreeObjectBase[] GetTree() => _output.ToArray().ConvertToTree(); internal void Clear() {