diff --git a/LiamBaseClasses/C4IT.LIAM.Base.cs b/LiamBaseClasses/C4IT.LIAM.Base.cs index 3993395..2af7118 100644 --- a/LiamBaseClasses/C4IT.LIAM.Base.cs +++ b/LiamBaseClasses/C4IT.LIAM.Base.cs @@ -32,6 +32,7 @@ namespace C4IT.LIAM Unknown = 0, NtfsShare = 101, NtfsFolder = 102, + DfsNamespaceRoot = 103, MsTeamsTeam = 401, MsTeamsChannel = 402, MsTeamsFolder = 403, @@ -396,4 +397,4 @@ namespace C4IT.LIAM public bool SupportsPermissions { get; set; } = false; } -} \ No newline at end of file +} diff --git a/LiamNtfs/C4IT.LIAM.Ntfs.cs b/LiamNtfs/C4IT.LIAM.Ntfs.cs index 63b9769..8a6dd74 100644 --- a/LiamNtfs/C4IT.LIAM.Ntfs.cs +++ b/LiamNtfs/C4IT.LIAM.Ntfs.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using C4IT.Logging; using C4IT.Matrix42.ServerInfo; +using C4IT_IAM; using C4IT_IAM_Engine; using C4IT_IAM_SET; using LiamNtfs; @@ -30,9 +31,30 @@ namespace C4IT.LIAM public class cLiamProviderNtfs : cLiamProviderBase { + private enum eNtfsPathKind + { + Unknown = 0, + ServerRoot = 1, + ClassicShare = 2, + DfsNamespaceRoot = 3, + DfsLink = 4, + Folder = 5 + } + + private sealed class cNtfsPathClassification + { + public string NormalizedPath { get; set; } = string.Empty; + public eNtfsPathKind Kind { get; set; } = eNtfsPathKind.Unknown; + public string BoundaryPath { get; set; } = string.Empty; + public string ParentBoundaryPath { get; set; } = string.Empty; + public string DisplayName { get; set; } = string.Empty; + public int Level { get; set; } = -1; + } + public static Guid nftsModuleId = new Guid("77e213a1-6517-ea11-4881-000c2980fd94"); public readonly cNtfsBase ntfsBase = new cNtfsBase(); public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase(); + private readonly Dictionary> publishedShareCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); //public readonly bool WithoutPrivateFolders = true; @@ -145,40 +167,15 @@ namespace C4IT.LIAM return null; var DataAreas = new List(); + var rootClassification = ClassifyPath(this.RootPath); + var rootDataArea = await BuildDataAreaAsync(rootClassification); + if (rootDataArea == null) + return null; - var rootpathSplit = this.RootPath.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries); - cLiamNtfsShare share = null; - cLiamNtfsFolder NtfsRootFolder = null; - switch (rootpathSplit.Length) - { - case 0: - case 1: - return null; - case 2: - { - share = new cLiamNtfsShare(this, new cNtfsResultShare() - { - DisplayName = rootpathSplit.Last(), - Path = RootPath, - Level = 0 - }); - await share.ResolvePermissionGroupsAsync(share.TechnicalName); - DataAreas.Add(share); - break; - } - default: - { - NtfsRootFolder = new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder() - { - DisplayName = rootpathSplit.Last(), - Path = RootPath, - Level = 0 - }); - await NtfsRootFolder.ResolvePermissionGroupsAsync(NtfsRootFolder.TechnicalName); - DataAreas.Add(NtfsRootFolder); - break; - } - } + DataAreas.Add(rootDataArea); + + if (Depth == 0) + return DataAreas; var DAL = await ntfsBase.RequestFoldersListAsync(this.RootPath, Depth); if (DAL == null) @@ -189,10 +186,10 @@ namespace C4IT.LIAM if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(Entry.Value.DisplayName, this.DataAreaRegEx).Success) continue; - - var Folder = new cLiamNtfsFolder(this, share, NtfsRootFolder, (cNtfsResultFolder)Entry.Value); - await Folder.ResolvePermissionGroupsAsync(Folder.TechnicalName); - DataAreas.Add(Folder); + var classification = ClassifyPath(Entry.Value.Path); + var dataArea = await BuildDataAreaAsync(classification, Entry.Value as cNtfsResultFolder); + if (dataArea != null) + DataAreas.Add(dataArea); } return DataAreas; } @@ -209,7 +206,6 @@ namespace C4IT.LIAM } public override async Task LoadDataArea(string UID) { - //TODO implement LoadDataArea var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try @@ -222,36 +218,8 @@ namespace C4IT.LIAM } if (!await LogonAsync()) return null; - var splt = UID.Split(System.IO.Path.DirectorySeparatorChar); - var name = Path.GetDirectoryName(UID); - switch (splt.Length) - { - case 0: - case 1: - return null; - case 2: - { - var share = new cLiamNtfsShare(this, new cNtfsResultShare() - { - DisplayName = name, - Path = UID, - Level = getDepth(UID) - }); - await share.ResolvePermissionGroupsAsync(share.TechnicalName); - return share; - } - default: - { - var folder = new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder() - { - DisplayName = name, - Path = UID, - Level = getDepth(UID) - }); - await folder.ResolvePermissionGroupsAsync(folder.TechnicalName); - return folder; - } - } + var classification = ClassifyPath(UID); + return await BuildDataAreaAsync(classification); } catch (Exception E) { @@ -265,6 +233,208 @@ namespace C4IT.LIAM } + private async Task BuildDataAreaAsync(cNtfsPathClassification classification, cNtfsResultFolder folderResult = null) + { + if (classification == null) + return null; + + switch (classification.Kind) + { + case eNtfsPathKind.ClassicShare: + case eNtfsPathKind.DfsLink: + { + var share = new cLiamNtfsShare(this, new cNtfsResultShare() + { + DisplayName = classification.DisplayName, + Path = classification.NormalizedPath, + Level = classification.Level + }, classification.ParentBoundaryPath); + await share.ResolvePermissionGroupsAsync(share.TechnicalName); + return share; + } + case eNtfsPathKind.DfsNamespaceRoot: + { + var namespaceRoot = new cLiamNtfsDfsNamespaceRoot(this, new cNtfsResultShare() + { + DisplayName = classification.DisplayName, + Path = classification.NormalizedPath, + Level = classification.Level + }); + await namespaceRoot.ResolvePermissionGroupsAsync(namespaceRoot.TechnicalName); + return namespaceRoot; + } + case eNtfsPathKind.Folder: + { + var folderData = folderResult ?? new cNtfsResultFolder() + { + DisplayName = classification.DisplayName, + Path = classification.NormalizedPath, + Level = classification.Level, + CreatedDate = Directory.Exists(classification.NormalizedPath) + ? new DirectoryInfo(classification.NormalizedPath).CreationTimeUtc.ToString("s") + : DateTime.MinValue.ToString("s") + }; + var folder = new cLiamNtfsFolder(this, null, null, folderData, classification.ParentBoundaryPath); + await folder.ResolvePermissionGroupsAsync(folder.TechnicalName); + return folder; + } + default: + return null; + } + } + + private cNtfsPathClassification ClassifyPath(string path) + { + var normalizedPath = NormalizeUncPath(path); + var segments = GetUncSegments(normalizedPath); + var classification = new cNtfsPathClassification() + { + NormalizedPath = normalizedPath, + DisplayName = GetDisplayName(normalizedPath), + Level = getDepth(normalizedPath) + }; + + if (segments.Length == 1) + { + classification.Kind = eNtfsPathKind.ServerRoot; + return classification; + } + + if (segments.Length < 2) + return classification; + + var serverName = segments[0]; + var publishedShares = GetPublishedShareNames(serverName); + var firstBoundaryPath = BuildUncPath(segments, 2); + + if (segments.Length == 2) + { + if (publishedShares.Contains(segments[1])) + { + classification.Kind = eNtfsPathKind.ClassicShare; + classification.BoundaryPath = normalizedPath; + return classification; + } + + if (Directory.Exists(normalizedPath)) + { + classification.Kind = eNtfsPathKind.DfsNamespaceRoot; + classification.BoundaryPath = normalizedPath; + } + + return classification; + } + + if (publishedShares.Contains(segments[1])) + { + classification.Kind = eNtfsPathKind.Folder; + classification.BoundaryPath = firstBoundaryPath; + classification.ParentBoundaryPath = segments.Length == 3 + ? firstBoundaryPath + : BuildUncPath(segments, segments.Length - 1); + return classification; + } + + if (Directory.Exists(firstBoundaryPath)) + { + if (segments.Length == 3) + { + classification.Kind = eNtfsPathKind.DfsLink; + classification.BoundaryPath = normalizedPath; + classification.ParentBoundaryPath = firstBoundaryPath; + return classification; + } + + classification.Kind = eNtfsPathKind.Folder; + classification.BoundaryPath = BuildUncPath(segments, 3); + classification.ParentBoundaryPath = segments.Length == 4 + ? BuildUncPath(segments, 3) + : BuildUncPath(segments, segments.Length - 1); + return classification; + } + + return classification; + } + + private string NormalizeUncPath(string path) + { + if (string.IsNullOrWhiteSpace(path)) + return string.Empty; + + var segments = path.Trim().Replace('/', '\\').Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + if (segments.Length == 0) + return string.Empty; + + return @"\\" + string.Join("\\", segments); + } + + private string[] GetUncSegments(string path) + { + var normalized = NormalizeUncPath(path); + return normalized.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + } + + private string BuildUncPath(string[] segments, int segmentCount) + { + if (segments == null || segmentCount <= 0 || segments.Length < segmentCount) + return string.Empty; + + return @"\\" + string.Join("\\", segments.Take(segmentCount)); + } + + private string GetDisplayName(string path) + { + var segments = GetUncSegments(path); + if (segments.Length == 0) + return string.Empty; + + return segments.Last(); + } + + private HashSet GetPublishedShareNames(string serverName) + { + HashSet shares; + if (publishedShareCache.TryGetValue(serverName, out shares)) + return shares; + + shares = new HashSet(StringComparer.OrdinalIgnoreCase); + try + { + using (var connection = new cNetworkConnection(this.RootPath, this.Credential?.Identification, this.Credential?.Secret)) + { + foreach (var share in connection.EnumNetShares(serverName)) + { + if (!IsVisibleDiskShare(share)) + continue; + + shares.Add(share.shi1_netname); + } + } + } + catch (Exception ex) + { + LogException(ex); + } + + publishedShareCache[serverName] = shares; + return shares; + } + + private bool IsVisibleDiskShare(C4IT_IAM.SHARE_INFO_1 share) + { + if (string.IsNullOrWhiteSpace(share.shi1_netname)) + return false; + + if (share.shi1_netname.StartsWith("ERROR=", StringComparison.OrdinalIgnoreCase)) + return false; + + if (share.shi1_netname.EndsWith("$", StringComparison.OrdinalIgnoreCase)) + return false; + + var shareType = share.shi1_type & 0xFF; + return shareType == (uint)C4IT_IAM.SHARE_TYPE.STYPE_DISKTREE; + } + public override async Task> getSecurityGroupsAsync(string groupFilter) { @@ -689,7 +859,7 @@ namespace C4IT.LIAM { private readonly cNtfsResultBase Share = null; - public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share) : + public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share, string parentPath = null) : base(Provider) { this.Share = Share; @@ -701,6 +871,8 @@ namespace C4IT.LIAM this.DataType = eLiamDataAreaTypes.NtfsShare; if (Directory.Exists(Share.Path)) this.CreatedDate = new DirectoryInfo(Share.Path).CreationTimeUtc.ToString("s"); + if (!string.IsNullOrWhiteSpace(parentPath)) + this.ParentUID = cLiamNtfsFolder.GetUniqueDataAreaID(parentPath); } internal async Task> getFolders() @@ -746,6 +918,30 @@ namespace C4IT.LIAM } } + public class cLiamNtfsDfsNamespaceRoot : cLiamNtfsPermissionDataAreaBase + { + private readonly cNtfsResultBase NamespaceRoot = null; + + public cLiamNtfsDfsNamespaceRoot(cLiamProviderNtfs Provider, cNtfsResultBase NamespaceRoot) : + base(Provider) + { + this.NamespaceRoot = NamespaceRoot; + + this.DisplayName = NamespaceRoot.Path.Split('\\').Last(); + this.TechnicalName = NamespaceRoot.Path; + this.UID = cLiamNtfsFolder.GetUniqueDataAreaID(NamespaceRoot.Path); + this.Level = NamespaceRoot.Level; + this.DataType = eLiamDataAreaTypes.DfsNamespaceRoot; + if (Directory.Exists(NamespaceRoot.Path)) + this.CreatedDate = new DirectoryInfo(NamespaceRoot.Path).CreationTimeUtc.ToString("s"); + } + + public override async Task> getChildrenAsync(int Depth = -1) + { + await Task.Delay(0); + return new List(); + } + } public class cLiamAdGroup : cLiamDataAreaBase { public new readonly cLiamProviderNtfs Provider = null; @@ -768,7 +964,7 @@ namespace C4IT.LIAM { public readonly cLiamNtfsShare Share = null; public readonly cLiamNtfsFolder NtfsRootFolder = null; - public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder) : base(Provider) + public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder, string parentPathOverride = null) : base(Provider) { var ntfsParent = NtfsFolder.Parent; this.NtfsRootFolder = ntfsRootFolder; @@ -787,6 +983,8 @@ namespace C4IT.LIAM { this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath); } + if (string.IsNullOrWhiteSpace(this.ParentUID) && !string.IsNullOrWhiteSpace(parentPathOverride)) + this.ParentUID = GetUniqueDataAreaID(parentPathOverride); } public static string GetUniqueDataAreaID(string fullPath) diff --git a/LiamWorkflowActivities/C4IT.LIAM.WorkflowactivityBase.cs b/LiamWorkflowActivities/C4IT.LIAM.WorkflowactivityBase.cs index 12573a9..4083845 100644 --- a/LiamWorkflowActivities/C4IT.LIAM.WorkflowactivityBase.cs +++ b/LiamWorkflowActivities/C4IT.LIAM.WorkflowactivityBase.cs @@ -628,7 +628,7 @@ namespace LiamWorkflowActivities if (!IsAdditionalConfigurationEnabled(providerEntry.Provider, "EnsureNtfsPermissionGroups")) return true; - foreach (var ntfsArea in dataAreas.OfType()) + foreach (var ntfsArea in dataAreas.OfType()) { var folderPath = ntfsArea.TechnicalName; if (string.IsNullOrWhiteSpace(folderPath))