1054 lines
43 KiB
C#
1054 lines
43 KiB
C#
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 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<string, HashSet<string>> publishedShareCache = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
//public readonly bool WithoutPrivateFolders = true;
|
|
|
|
public cLiamProviderNtfs(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
|
|
base(LiamConfiguration, ProviderData)
|
|
{
|
|
this.ReplaceNtfsCustomTags();
|
|
}
|
|
|
|
public override async Task<bool> 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<bool> 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<List<cLiamDataAreaBase>> 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<cLiamDataAreaBase>();
|
|
}
|
|
if (!await LogonAsync())
|
|
return null;
|
|
if (string.IsNullOrEmpty(this.RootPath))
|
|
return null;
|
|
|
|
var DataAreas = new List<cLiamDataAreaBase>();
|
|
var rootClassification = ClassifyPath(this.RootPath);
|
|
var rootDataArea = await BuildDataAreaAsync(rootClassification);
|
|
if (rootDataArea == null)
|
|
return null;
|
|
|
|
DataAreas.Add(rootDataArea);
|
|
|
|
if (Depth == 0)
|
|
return DataAreas;
|
|
|
|
var DAL = await ntfsBase.RequestFoldersListAsync(this.RootPath, Depth);
|
|
if (DAL == null)
|
|
return null;
|
|
|
|
foreach (var Entry in DAL)
|
|
{
|
|
if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(Entry.Value.DisplayName, this.DataAreaRegEx).Success)
|
|
continue;
|
|
|
|
var classification = ClassifyPath(Entry.Value.Path);
|
|
var dataArea = await BuildDataAreaAsync(classification, Entry.Value as cNtfsResultFolder);
|
|
if (dataArea != null)
|
|
DataAreas.Add(dataArea);
|
|
}
|
|
return DataAreas;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
public override async Task<cLiamDataAreaBase> 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<cLiamDataAreaBase> 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<string> GetPublishedShareNames(string serverName)
|
|
{
|
|
HashSet<string> shares;
|
|
if (publishedShareCache.TryGetValue(serverName, out shares))
|
|
return shares;
|
|
|
|
shares = new HashSet<string>(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<List<cLiamDataAreaBase>> 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<cLiamDataAreaBase>();
|
|
}
|
|
if (!await LogonAsync())
|
|
return null;
|
|
if (string.IsNullOrEmpty(this.GroupPath))
|
|
return null;
|
|
|
|
var SecurityGroups = new List<cLiamDataAreaBase>();
|
|
|
|
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<ResultToken> CreateDataAreaAsync(
|
|
string newFolderPath,
|
|
string newFolderParent,
|
|
IDictionary<string, string> customTags,
|
|
IEnumerable<string> ownerSids,
|
|
IEnumerable<string> readerSids,
|
|
IEnumerable<string> writerSids
|
|
)
|
|
{
|
|
var engine = CreateFilesystemEngine(
|
|
newFolderPath,
|
|
newFolderParent,
|
|
customTags,
|
|
ownerSids,
|
|
readerSids,
|
|
writerSids);
|
|
var result = engine.createDataArea();
|
|
return Task.FromResult(result);
|
|
}
|
|
|
|
public Task<ResultToken> EnsureMissingPermissionGroupsAsync(
|
|
string folderPath,
|
|
IDictionary<string, string> customTags,
|
|
IEnumerable<string> ownerSids,
|
|
IEnumerable<string> readerSids,
|
|
IEnumerable<string> writerSids,
|
|
bool ensureTraverseGroups = false)
|
|
{
|
|
var parentPath = Directory.GetParent(folderPath)?.FullName;
|
|
var engine = CreateFilesystemEngine(
|
|
folderPath,
|
|
parentPath,
|
|
customTags,
|
|
ownerSids,
|
|
readerSids,
|
|
writerSids);
|
|
|
|
return Task.FromResult(engine.ensureDataAreaPermissions(ensureTraverseGroups));
|
|
}
|
|
|
|
private DataArea_FileSystem CreateFilesystemEngine(
|
|
string folderPath,
|
|
string parentFolderPath,
|
|
IDictionary<string, string> customTags,
|
|
IEnumerable<string> ownerSids,
|
|
IEnumerable<string> readerSids,
|
|
IEnumerable<string> writerSids)
|
|
{
|
|
var requiresDomainLocalTag = this.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP
|
|
|| (NamingConventions ?? Enumerable.Empty<cLiamNamingConvention>())
|
|
.Any(i => i.Scope == eLiamAccessRoleScopes.DomainLocal);
|
|
|
|
var mergedCustomTags = new Dictionary<string, string>(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<string>(),
|
|
readerUserSids = readerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
|
writerUserSids = writerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
|
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"),
|
|
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);
|
|
}
|
|
|
|
private IEnumerable<IAM_SecurityGroupTemplate> BuildSecurityGroupTemplates()
|
|
{
|
|
var templates = new List<IAM_SecurityGroupTemplate>();
|
|
foreach (var namingConvention in NamingConventions ?? Enumerable.Empty<cLiamNamingConvention>())
|
|
{
|
|
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)
|
|
{
|
|
return getDepth(new DirectoryInfo(root), new DirectoryInfo(folder));
|
|
|
|
}
|
|
|
|
public override string GetLastErrorMessage()
|
|
{
|
|
var messages = new List<string>();
|
|
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<List<cLiamUserInfo>> 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<List<cLiamUserInfo>> 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<cLiamUserInfo>(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<List<cLiamDataAreaBase>> getFolders()
|
|
{
|
|
var CM = MethodBase.GetCurrentMethod();
|
|
LogMethodBegin(CM);
|
|
try
|
|
{
|
|
await Task.Delay(0);
|
|
var RetVal = new List<cLiamDataAreaBase>(0);
|
|
return RetVal;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
return null;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
|
|
{
|
|
var CM = MethodBase.GetCurrentMethod();
|
|
LogMethodBegin(CM);
|
|
try
|
|
{
|
|
await Task.Delay(0);
|
|
var RetVal = new List<cLiamDataAreaBase>();
|
|
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<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
|
|
{
|
|
await Task.Delay(0);
|
|
return new List<cLiamDataAreaBase>();
|
|
}
|
|
}
|
|
public class cLiamAdGroup : cLiamDataAreaBase
|
|
{
|
|
public new readonly cLiamProviderNtfs Provider = null;
|
|
public readonly string dn = null;
|
|
public readonly string scope = null;
|
|
public override Task<List<cLiamDataAreaBase>> 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 (ntfsParent != null)
|
|
{
|
|
this.ParentUID = GetUniqueDataAreaID(ntfsParent.Path);
|
|
}
|
|
else if (this.Level == 1)
|
|
{
|
|
this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath);
|
|
}
|
|
if (string.IsNullOrWhiteSpace(this.ParentUID) && !string.IsNullOrWhiteSpace(parentPathOverride))
|
|
this.ParentUID = GetUniqueDataAreaID(parentPathOverride);
|
|
}
|
|
|
|
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<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = 1)
|
|
{
|
|
//TODO implement getChildrenAsync
|
|
var CM = MethodBase.GetCurrentMethod();
|
|
LogMethodBegin(CM);
|
|
try
|
|
{
|
|
await Task.Delay(0);
|
|
var DataAreas = new List<cLiamDataAreaBase>();
|
|
|
|
return DataAreas;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
return null;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|