using System; using System.Collections.Generic; using System.DirectoryServices.AccountManagement; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Security.AccessControl; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using C4IT.Logging; using C4IT.Matrix42.ServerInfo; using C4IT_IAM; using C4IT_IAM_Engine; using C4IT_IAM_SET; using LiamNtfs; using static C4IT.Logging.cLogManager; using static LiamNtfs.cActiveDirectoryBase; namespace C4IT.LIAM { public static class LiamInitializer { static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) { return new cLiamProviderNtfs(LiamConfiguration, ProviderData); } } 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 ParentPath { 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); private readonly Dictionary dfsEntryPathCache = new Dictionary(StringComparer.OrdinalIgnoreCase); //public readonly bool WithoutPrivateFolders = true; public cLiamProviderNtfs(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) : base(LiamConfiguration, ProviderData) { this.ReplaceNtfsCustomTags(); } public override async Task LogonAsync() { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return false; } return await LogonAsync(true); } private void ReplaceNtfsCustomTags() { foreach (var namingConvention in NamingConventions) { String key = null; String value = null; key = "GROUPTYPEPOSTFIX"; if (namingConvention.AccessRole == eLiamAccessRoles.Owner) { value = CustomTags["Filesystem_GroupOwnerTag"]; } else if (namingConvention.AccessRole == eLiamAccessRoles.Write) { value = CustomTags["Filesystem_GroupWriteTag"]; } else if (namingConvention.AccessRole == eLiamAccessRoles.Read) { value = CustomTags["Filesystem_GroupReadTag"]; } else if (namingConvention.AccessRole == eLiamAccessRoles.Traverse) { value = CustomTags["Filesystem_GroupTraverseTag"]; } if (!String.IsNullOrEmpty(key) && !String.IsNullOrEmpty(value)) { namingConvention.DescriptionTemplate = namingConvention.DescriptionTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.NamingTemplate = namingConvention.NamingTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.Wildcard = namingConvention.Wildcard.Replace($"{{{{{key}}}}}", value); } value = null; key = "SCOPE"; if (namingConvention.Scope == eLiamAccessRoleScopes.DomainLocal) { value = CustomTags["Filesystem_GroupDomainLocalTag"]; } else if (namingConvention.Scope == eLiamAccessRoleScopes.Global) { value = CustomTags["Filesystem_GroupGlobalTag"]; } if (!String.IsNullOrEmpty(key) && !String.IsNullOrEmpty(value)) { namingConvention.DescriptionTemplate = namingConvention.DescriptionTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.NamingTemplate = namingConvention.NamingTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.Wildcard = namingConvention.Wildcard.Replace($"{{{{{key}}}}}", value); } } } public async Task LogonAsync(bool force = false) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var LI = new cNtfsLogonInfo() { Domain = Domain, User = Credential?.Identification, UserSecret = Credential?.Secret, TargetNetworkName = RootPath, TargetGroupPath = this.GroupPath }; var RetVal = await ntfsBase.LogonAsync(LI) && await activeDirectoryBase.LogonAsync(LI); return RetVal; } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } return false; } public override async Task> getDataAreasAsync(int Depth = -1) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return new List(); } if (!await LogonAsync()) return null; if (string.IsNullOrEmpty(this.RootPath)) return null; var DataAreas = new List(); var rootClassification = ClassifyPath(this.RootPath); var rootDataArea = await BuildDataAreaAsync(rootClassification); if (rootDataArea == null) return null; DataAreas.Add(rootDataArea); if (Depth == 0) return DataAreas; DataAreas.AddRange(await GetChildDataAreasAsync(rootClassification, Depth)); return DataAreas; } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } return null; } public override async Task LoadDataArea(string UID) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return null; } if (!await LogonAsync()) return null; var classification = ClassifyPath(UID); return await BuildDataAreaAsync(classification); } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private async Task BuildDataAreaAsync(cNtfsPathClassification classification, cNtfsResultFolder folderResult = null) { if (classification == null) return null; switch (classification.Kind) { case eNtfsPathKind.ServerRoot: { return new cLiamNtfsServerRoot(this, classification.NormalizedPath, classification.Level); } 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") }; folderData.Level = classification.Level; if (folderData.Parent == null && !string.IsNullOrWhiteSpace(classification.ParentPath)) folderData.Parent = new cNtfsResultFolder() { Path = classification.ParentPath }; var parentPath = !string.IsNullOrWhiteSpace(classification.ParentPath) ? classification.ParentPath : classification.ParentBoundaryPath; var folder = new cLiamNtfsFolder(this, null, null, folderData, parentPath); 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; classification.ParentPath = segments.Length > 2 ? BuildUncPath(segments, segments.Length - 1) : string.Empty; var dfsPrefixes = GetDfsObjectPrefixes(normalizedPath); if (dfsPrefixes.Count > 0) { var namespaceRootPath = dfsPrefixes[0]; var deepestDfsPath = dfsPrefixes[dfsPrefixes.Count - 1]; if (PathsEqual(normalizedPath, namespaceRootPath)) { classification.Kind = eNtfsPathKind.DfsNamespaceRoot; classification.BoundaryPath = normalizedPath; return classification; } if (PathsEqual(normalizedPath, deepestDfsPath)) { classification.Kind = eNtfsPathKind.DfsLink; classification.BoundaryPath = deepestDfsPath; classification.ParentBoundaryPath = dfsPrefixes.Count > 1 ? dfsPrefixes[dfsPrefixes.Count - 2] : namespaceRootPath; return classification; } classification.Kind = eNtfsPathKind.Folder; classification.BoundaryPath = deepestDfsPath; classification.ParentBoundaryPath = classification.ParentPath; return classification; } var shareBoundaryPath = GetPublishedShareBoundaryPath(segments); if (!string.IsNullOrWhiteSpace(shareBoundaryPath)) { if (PathsEqual(normalizedPath, shareBoundaryPath)) { classification.Kind = eNtfsPathKind.ClassicShare; classification.BoundaryPath = shareBoundaryPath; return classification; } classification.Kind = eNtfsPathKind.Folder; classification.BoundaryPath = shareBoundaryPath; classification.ParentBoundaryPath = classification.ParentPath; return classification; } if (Directory.Exists(normalizedPath)) { classification.Kind = eNtfsPathKind.Folder; classification.ParentBoundaryPath = classification.ParentPath; } return classification; } private async Task> GetChildDataAreasAsync(cNtfsPathClassification parentClassification, int depth) { var children = new List(); if (parentClassification == null || depth == 0) return children; if (parentClassification.Kind == eNtfsPathKind.ServerRoot) { foreach (var childPath in GetServerRootChildPaths(parentClassification.NormalizedPath)) { var childClassification = ClassifyPath(childPath); if (!ShouldIncludeDataArea(childClassification)) continue; var childDataArea = await BuildDataAreaAsync(childClassification); if (childDataArea == null) continue; children.Add(childDataArea); if (depth > 1) children.AddRange(await GetChildDataAreasAsync(childClassification, depth - 1)); } return children; } var folderEntries = await ntfsBase.RequestFoldersListAsync(parentClassification.NormalizedPath, 1); if (folderEntries == null) return children; foreach (var entry in folderEntries.Values.OfType()) { var childClassification = ClassifyPath(entry.Path); if (!ShouldIncludeDataArea(childClassification)) continue; var childDataArea = await BuildDataAreaAsync(childClassification, entry); if (childDataArea == null) continue; children.Add(childDataArea); if (depth > 1) children.AddRange(await GetChildDataAreasAsync(childClassification, depth - 1)); } return children; } private IEnumerable GetServerRootChildPaths(string serverRootPath) { var segments = GetUncSegments(serverRootPath); if (segments.Length != 1) return Enumerable.Empty(); var serverName = segments[0]; return GetPublishedShareNames(serverName) .OrderBy(i => i, StringComparer.OrdinalIgnoreCase) .Select(shareName => BuildUncPath(new[] { serverName, shareName }, 2)); } private bool ShouldIncludeDataArea(cNtfsPathClassification classification) { if (classification == null) return false; if (!MatchesDataAreaRegEx(classification.DisplayName)) return false; string matchingConfigurationKey; string matchingRule; if (IsBlacklistedFolderPath(classification, out matchingConfigurationKey, out matchingRule)) { LogEntry($"Skip NTFS path '{classification.NormalizedPath}' due to AdditionalConfiguration rule '{matchingConfigurationKey}={matchingRule}'", LogLevels.Debug); return false; } return true; } private bool MatchesDataAreaRegEx(string displayName) { if (string.IsNullOrEmpty(this.DataAreaRegEx)) return true; return Regex.Match(displayName ?? string.Empty, this.DataAreaRegEx).Success; } private bool IsBlacklistedFolderPath(cNtfsPathClassification classification, out string matchingConfigurationKey, out string matchingRule) { matchingConfigurationKey = null; matchingRule = null; if (classification == null || classification.Kind != eNtfsPathKind.Folder) return false; foreach (var excludedFolderName in GetAdditionalConfigurationValues("NtfsExcludeFolderNames")) { if (!MatchesAdditionalConfigurationPattern(classification.DisplayName, excludedFolderName)) continue; matchingConfigurationKey = "NtfsExcludeFolderNames"; matchingRule = excludedFolderName; return true; } var relativePath = GetRelativePathFromRoot(classification.NormalizedPath); foreach (var excludedRelativePath in GetAdditionalConfigurationValues("NtfsExcludeRelativePaths")) { if (!MatchesAdditionalConfigurationPattern(relativePath, excludedRelativePath)) continue; matchingConfigurationKey = "NtfsExcludeRelativePaths"; matchingRule = excludedRelativePath; return true; } return false; } private IEnumerable GetAdditionalConfigurationValues(string key) { if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key)) return Enumerable.Empty(); string rawValue; if (!AdditionalConfiguration.TryGetValue(key, out rawValue) || string.IsNullOrWhiteSpace(rawValue)) return Enumerable.Empty(); return rawValue .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) .Select(i => i.Trim()) .Where(i => !string.IsNullOrWhiteSpace(i)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } private string GetRelativePathFromRoot(string path) { var normalizedRoot = NormalizeUncPath(this.RootPath); var normalizedPath = NormalizeUncPath(path); if (string.IsNullOrWhiteSpace(normalizedRoot) || string.IsNullOrWhiteSpace(normalizedPath)) return string.Empty; if (PathsEqual(normalizedRoot, normalizedPath)) return string.Empty; var rootWithSeparator = normalizedRoot + "\\"; if (!normalizedPath.StartsWith(rootWithSeparator, StringComparison.OrdinalIgnoreCase)) return normalizedPath; return normalizedPath.Substring(rootWithSeparator.Length) .Trim() .TrimStart('\\') .Replace('/', '\\'); } private bool MatchesAdditionalConfigurationPattern(string value, string pattern) { if (string.IsNullOrWhiteSpace(value) || string.IsNullOrWhiteSpace(pattern)) return false; var normalizedValue = value.Trim().Replace('/', '\\'); var normalizedPattern = pattern.Trim().Replace('/', '\\').Trim('\\'); if (string.IsNullOrWhiteSpace(normalizedPattern)) return false; var regexPattern = "^" + Regex.Escape(normalizedPattern).Replace("\\*", ".*") + "$"; return Regex.IsMatch(normalizedValue, regexPattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } private List GetDfsObjectPrefixes(string path) { var normalizedPath = NormalizeUncPath(path); var segments = GetUncSegments(normalizedPath); var prefixes = new List(); for (var segmentCount = 2; segmentCount <= segments.Length; segmentCount++) { var prefix = BuildUncPath(segments, segmentCount); string entryPath; if (!TryGetDfsEntryPath(prefix, out entryPath)) continue; prefixes.Add(!string.IsNullOrWhiteSpace(entryPath) ? NormalizeUncPath(entryPath) : prefix); } return prefixes .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(i => GetUncSegments(i).Length) .ToList(); } private bool TryGetDfsEntryPath(string path, out string entryPath) { var normalizedPath = NormalizeUncPath(path); if (dfsEntryPathCache.TryGetValue(normalizedPath, out entryPath)) return !string.IsNullOrWhiteSpace(entryPath); string resolvedEntryPath; if (cNetworkConnection.TryGetDfsEntryPath(normalizedPath, out resolvedEntryPath)) { entryPath = NormalizeUncPath(string.IsNullOrWhiteSpace(resolvedEntryPath) ? normalizedPath : resolvedEntryPath); dfsEntryPathCache[normalizedPath] = entryPath; return true; } dfsEntryPathCache[normalizedPath] = string.Empty; entryPath = string.Empty; return false; } private string GetPublishedShareBoundaryPath(string[] segments) { if (segments == null || segments.Length < 2) return string.Empty; var serverName = segments[0]; var publishedShares = GetPublishedShareNames(serverName); if (!publishedShares.Contains(segments[1])) return string.Empty; return BuildUncPath(segments, 2); } private bool PathsEqual(string left, string right) { return string.Equals( NormalizeUncPath(left), NormalizeUncPath(right), StringComparison.OrdinalIgnoreCase); } 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) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return new List(); } if (!await LogonAsync()) return null; if (string.IsNullOrEmpty(this.GroupPath)) return null; var SecurityGroups = new List(); var SGL = await activeDirectoryBase.RequestSecurityGroupsListAsync(groupFilter); if (SGL == null) return null; foreach (var Entry in SGL) { if (!string.IsNullOrEmpty(this.GroupRegEx) && !Regex.Match(Entry.Value.DisplayName, this.GroupRegEx).Success) continue; var SecurityGroup = new cLiamAdGroup(this, (cSecurityGroupResult)Entry.Value); SecurityGroups.Add(SecurityGroup); } return SecurityGroups; } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } return null; } public Task CreateDataAreaAsync( string newFolderPath, string newFolderParent, IDictionary customTags, IEnumerable ownerSids, IEnumerable readerSids, IEnumerable writerSids, bool whatIf = false ) { var engine = CreateFilesystemEngine( newFolderPath, newFolderParent, customTags, ownerSids, readerSids, writerSids); engine.WhatIf = whatIf; var result = engine.createDataArea(); return Task.FromResult(result); } public Task EnsureMissingPermissionGroupsAsync( string folderPath, IDictionary customTags, IEnumerable ownerSids, IEnumerable readerSids, IEnumerable writerSids, bool ensureTraverseGroups = false, bool whatIf = false) { if (!IsPermissionManagedFolderPath(folderPath)) { return Task.FromResult(new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString()) { resultErrorId = 30008, resultMessage = $"NTFS permission ensure is only supported for folder paths. Shares, DFS namespaces and server roots are skipped: {folderPath}" }); } var parentPath = Directory.GetParent(folderPath)?.FullName; var engine = CreateFilesystemEngine( folderPath, parentPath, customTags, ownerSids, readerSids, writerSids); engine.WhatIf = whatIf; return Task.FromResult(engine.ensureDataAreaPermissions(ensureTraverseGroups)); } private DataArea_FileSystem CreateFilesystemEngine( string folderPath, string parentFolderPath, IDictionary customTags, IEnumerable ownerSids, IEnumerable readerSids, IEnumerable writerSids) { var requiresDomainLocalTag = this.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP || (NamingConventions ?? Enumerable.Empty()) .Any(i => i.Scope == eLiamAccessRoleScopes.DomainLocal); var mergedCustomTags = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var tag in CustomTags) mergedCustomTags[tag.Key] = tag.Value; if (customTags != null) { foreach (var tag in customTags) mergedCustomTags[tag.Key] = tag.Value; } var engine = new DataArea_FileSystem { ConfigID = "manual", domainName = this.Domain, username = this.Credential.Identification, password = new NetworkCredential("", this.Credential.Secret).SecurePassword, baseFolder = this.RootPath, newFolderPath = folderPath, newFolderParent = parentFolderPath, groupPrefix = GetRequiredCustomTag("Filesystem_GroupPrefixTag"), groupOUPath = this.GroupPath, groupPermissionStrategy = (C4IT_IAM_GET.PermissionGroupStrategy)this.GroupStrategy, groupCustomTags = mergedCustomTags, ownerUserSids = ownerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List(), readerUserSids = readerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List(), writerUserSids = writerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List(), groupOwnerTag = GetRequiredCustomTag("Filesystem_GroupOwnerTag"), groupWriteTag = GetRequiredCustomTag("Filesystem_GroupWriteTag"), groupReadTag = GetRequiredCustomTag("Filesystem_GroupReadTag"), groupTraverseTag = GetRequiredCustomTag("Filesystem_GroupTraverseTag"), groupDLTag = requiresDomainLocalTag ? GetRequiredCustomTag("Filesystem_GroupDomainLocalTag") : string.Empty, groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag"), CanManagePermissionsForPath = IsPermissionManagedFolderPath, forceStrictAdGroupNames = IsAdditionalConfigurationEnabled("ForceStrictAdGroupNames") }; foreach (var template in BuildSecurityGroupTemplates()) engine.templates.Add(template); return engine; } private bool IsAdditionalConfigurationEnabled(string key) { if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key)) return false; if (!AdditionalConfiguration.TryGetValue(key, out var rawValue) || string.IsNullOrWhiteSpace(rawValue)) return false; return rawValue.Equals("true", StringComparison.OrdinalIgnoreCase) || rawValue.Equals("1", StringComparison.OrdinalIgnoreCase) || rawValue.Equals("yes", StringComparison.OrdinalIgnoreCase); } public bool IsPermissionManagedFolderPath(string path) { var classification = ClassifyPath(path); if (classification == null || classification.Kind != eNtfsPathKind.Folder) return false; string matchingConfigurationKey; string matchingRule; return !IsBlacklistedFolderPath(classification, out matchingConfigurationKey, out matchingRule); } private IEnumerable BuildSecurityGroupTemplates() { var templates = new List(); foreach (var namingConvention in NamingConventions ?? Enumerable.Empty()) { if (!TryMapSecurityGroupType(namingConvention.AccessRole, out var securityGroupType)) continue; if (!TryMapGroupScope(namingConvention.Scope, securityGroupType, out var groupScope)) continue; templates.Add(new IAM_SecurityGroupTemplate( namingConvention.NamingTemplate, namingConvention.DescriptionTemplate, namingConvention.Wildcard, securityGroupType, groupScope)); } return templates; } private bool TryMapSecurityGroupType(eLiamAccessRoles accessRole, out SecurityGroupType securityGroupType) { securityGroupType = SecurityGroupType.Read; switch (accessRole) { case eLiamAccessRoles.Owner: securityGroupType = SecurityGroupType.Owner; return true; case eLiamAccessRoles.Write: securityGroupType = SecurityGroupType.Write; return true; case eLiamAccessRoles.Read: securityGroupType = SecurityGroupType.Read; return true; case eLiamAccessRoles.Traverse: securityGroupType = SecurityGroupType.Traverse; return true; default: return false; } } private bool TryMapGroupScope(eLiamAccessRoleScopes scope, SecurityGroupType type, out GroupScope groupScope) { groupScope = GroupScope.Global; switch (scope) { case eLiamAccessRoleScopes.Global: groupScope = GroupScope.Global; return true; case eLiamAccessRoleScopes.DomainLocal: groupScope = GroupScope.Local; return true; case eLiamAccessRoleScopes.Unknown: if (type == SecurityGroupType.Traverse) { groupScope = this.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP ? GroupScope.Local : GroupScope.Global; return true; } return false; default: return false; } } private string GetRequiredCustomTag(string key) { if (CustomTags.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value)) return value; if (string.Equals(key, "Filesystem_GroupPrefixTag", StringComparison.OrdinalIgnoreCase) && CustomTags.TryGetValue("ADGroupPrefix", out value) && !string.IsNullOrWhiteSpace(value)) { return value; } throw new InvalidOperationException($"Missing NTFS custom tag '{key}'."); } public int getDepth(string path) { return getDepth(this.RootPath, path); } public static int getDepth(DirectoryInfo root, DirectoryInfo folder) { var rootDepth = root.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length; var folderDepth = folder.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length; return folderDepth - rootDepth; } public static int getDepth(string root, string folder) { if (string.IsNullOrWhiteSpace(root) || string.IsNullOrWhiteSpace(folder)) return -1; var rootSegments = root.Trim().Replace('/', '\\').Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); var folderSegments = folder.Trim().Replace('/', '\\').Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); return folderSegments.Length - rootSegments.Length; } public override string GetLastErrorMessage() { var messages = new List(); if (!string.IsNullOrEmpty(ntfsBase?.LastErrorMessage)) messages.Add(ntfsBase.LastErrorMessage); if (!string.IsNullOrEmpty(activeDirectoryBase?.LastErrorMessage)) messages.Add(activeDirectoryBase.LastErrorMessage); return messages.Count > 0 ? string.Join(" | ", messages) : null; } } public abstract class cLiamNtfsPermissionDataAreaBase : cLiamDataAreaBase { public new readonly cLiamProviderNtfs Provider = null; public string OwnerGroupIdentifier = "S-1-0-0"; public string WriteGroupIdentifier = "S-1-0-0"; public string ReadGroupIdentifier = "S-1-0-0"; public string TraverseGroupIdentifier = "S-1-0-0"; protected cLiamNtfsPermissionDataAreaBase(cLiamProviderNtfs Provider) : base(Provider) { this.Provider = Provider; this.SupportsOwners = true; this.SupportsPermissions = true; } public override async Task> GetOwnersAsync() { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { return await GetMembersAsync(true); } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } protected async Task> GetMembersAsync(bool owners) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var AD = this.Provider?.activeDirectoryBase; if (AD == null) { LogEntry($"Could not get ad class from Provider for data area '{this.TechnicalName}'", LogLevels.Warning); return null; } cADCollectionBase lstMembers; this.OwnerGroupIdentifier = this.OwnerRef ?? this.OwnerGroupIdentifier; if (owners && !String.IsNullOrEmpty(this.OwnerGroupIdentifier)) lstMembers = await AD.GetMembersAsync(this.OwnerGroupIdentifier); else lstMembers = null; if (lstMembers == null) { LogEntry($"Could not get owner list for data area '{this.TechnicalName}'", LogLevels.Warning); return null; } var RetVal = new List(lstMembers.Count); LogEntry($"Owners for data area found: {lstMembers.Count}", LogLevels.Debug); foreach (var MemberEntry in lstMembers.Values) { var User = new cLiamUserInfo() { DisplayName = MemberEntry.DisplayName, UserPrincipalName = (MemberEntry as cADUserResult).UserPrincipalName, SID = MemberEntry.ID }; RetVal.Add(User); } return RetVal; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } public async Task ResolvePermissionGroupsAsync(string path) { var ACLs = Provider.activeDirectoryBase.GetAccessControlList(path); if (ACLs == null) return; var ownerNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Owner && (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGP && i.Scope == eLiamAccessRoleScopes.Global || Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP && i.Scope == eLiamAccessRoleScopes.DomainLocal)); var writeNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Write && (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGP && i.Scope == eLiamAccessRoleScopes.Global || Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP && i.Scope == eLiamAccessRoleScopes.DomainLocal)); var readNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Read && (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGP && i.Scope == eLiamAccessRoleScopes.Global || Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP && i.Scope == eLiamAccessRoleScopes.DomainLocal)); var traverseNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Traverse); foreach (FileSystemAccessRule rule in ACLs) { if (rule.IdentityReference.Value == "S-1-1-0") continue; GroupPrincipal grp = GroupPrincipal.FindByIdentity(Provider.activeDirectoryBase.adContext, IdentityType.Sid, rule.IdentityReference.Value); if (grp == null) continue; DefaultLogger.LogEntry(LogLevels.Debug, $"Try matching: {grp.Name}"); if (Regex.IsMatch(grp.SamAccountName, ownerNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { this.OwnerGroupIdentifier = rule.IdentityReference.Value; if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) { var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var ownerNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Owner && i.Scope == eLiamAccessRoleScopes.Global); foreach (var memberItem in res) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, ownerNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) this.OwnerGroupIdentifier = SecurityGroup.UID; } } } else if (Regex.IsMatch(grp.SamAccountName, writeNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { this.WriteGroupIdentifier = rule.IdentityReference.Value; if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) { var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var writeNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Write && i.Scope == eLiamAccessRoleScopes.Global); foreach (var memberItem in res) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, writeNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) this.WriteGroupIdentifier = SecurityGroup.UID; } } } else if (Regex.IsMatch(grp.SamAccountName, readNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { this.ReadGroupIdentifier = rule.IdentityReference.Value; if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) { var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var readNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Read && i.Scope == eLiamAccessRoleScopes.Global); foreach (var memberItem in res) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, readNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) this.ReadGroupIdentifier = SecurityGroup.UID; } } } else if (Regex.IsMatch(grp.SamAccountName, traverseNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { this.TraverseGroupIdentifier = rule.IdentityReference.Value; } else { DefaultLogger.LogEntry(LogLevels.Debug, $"No match for: {grp.Name}"); } } } } public class cLiamNtfsShare : cLiamNtfsPermissionDataAreaBase { private readonly cNtfsResultBase Share = null; public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share, string parentPath = null) : base(Provider) { this.Share = Share; this.DisplayName = Share.Path.Split('\\').Last(); this.TechnicalName = Share.Path; this.UID = cLiamNtfsFolder.GetUniqueDataAreaID(Share.Path); this.Level = Share.Level; 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() { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var RetVal = new List(0); return RetVal; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } public override async Task> getChildrenAsync(int Depth = -1) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var RetVal = new List(); return RetVal; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } } 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 cLiamNtfsServerRoot : cLiamDataAreaBase { public new readonly cLiamProviderNtfs Provider = null; public cLiamNtfsServerRoot(cLiamProviderNtfs Provider, string path, int level) : base(Provider) { this.Provider = Provider; this.DisplayName = path.Split('\\').Last(); this.TechnicalName = path; this.UID = cLiamNtfsFolder.GetUniqueDataAreaID(path); this.Level = level; this.DataType = eLiamDataAreaTypes.NtfsServerRoot; } 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; public readonly string dn = null; public readonly string scope = null; public override Task> getChildrenAsync(int Depth = -1) { throw new NotImplementedException(); } public cLiamAdGroup(cLiamProviderNtfs Provider, cSecurityGroupResult secGroup) : base(Provider) { this.UID = secGroup.ID; this.TechnicalName = secGroup.DisplayName; this.Provider = Provider; this.dn = secGroup.Path; this.scope = secGroup.Scope.ToString(); } } public class cLiamNtfsFolder : cLiamNtfsPermissionDataAreaBase { public readonly cLiamNtfsShare Share = null; public readonly cLiamNtfsFolder NtfsRootFolder = null; public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder, string parentPathOverride = null) : base(Provider) { var ntfsParent = NtfsFolder.Parent; this.NtfsRootFolder = ntfsRootFolder; this.Share = share; this.TechnicalName = NtfsFolder.Path; this.UID =GetUniqueDataAreaID(NtfsFolder.Path); this.DisplayName = new DirectoryInfo(NtfsFolder.Path).Name; this.Level = NtfsFolder.Level; this.DataType = eLiamDataAreaTypes.NtfsFolder; this.CreatedDate = NtfsFolder.CreatedDate; if (!string.IsNullOrWhiteSpace(parentPathOverride)) { this.ParentUID = GetUniqueDataAreaID(parentPathOverride); } else if (ntfsParent != null) { this.ParentUID = GetUniqueDataAreaID(ntfsParent.Path); } else if (this.Level == 1) { this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath); } } public static string GetUniqueDataAreaID(string fullPath) { LogMethodBegin(MethodBase.GetCurrentMethod()); try { var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); var utf8 = new System.Text.UTF8Encoding(); var hash = BitConverter.ToString(md5.ComputeHash(utf8.GetBytes(fullPath))); hash = hash.ToLower().Replace("-", ""); return hash; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); throw; } finally { LogMethodEnd(MethodBase.GetCurrentMethod()); } } public string GetUniqueDataAreaID() { return GetUniqueDataAreaID(this.TechnicalName); } public override async Task> getChildrenAsync(int Depth = 1) { //TODO implement getChildrenAsync var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var DataAreas = new List(); return DataAreas; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } } }