fix: await NTFS ACL resolution for root data areas

This commit is contained in:
Meik
2026-03-09 09:11:31 +01:00
parent 9a20f3d1f0
commit e4b907856c
3 changed files with 452 additions and 432 deletions

View File

@@ -27,6 +27,7 @@ History is minimal (`initial`), so prefer concise, imperative commit subjects an
- After completing a requested code change, always create a Git commit with a concise, fitting, imperative message. - After completing a requested code change, always create a Git commit with a concise, fitting, imperative message.
- After completing a requested code change, always push the new commit immediately to the configured remote branch. - After completing a requested code change, always push the new commit immediately to the configured remote branch.
- Use Windows line endings (`CRLF`) for all text files in this repository. - Use Windows line endings (`CRLF`) for all text files in this repository.
- Every newly created or modified text file must be written back with Windows line endings (`CRLF`).
## Security & Configuration Tips ## Security & Configuration Tips

View File

@@ -162,6 +162,7 @@ namespace C4IT.LIAM
Path = RootPath, Path = RootPath,
Level = 0 Level = 0
}); });
await share.ResolvePermissionGroupsAsync(share.TechnicalName);
DataAreas.Add(share); DataAreas.Add(share);
break; break;
} }
@@ -173,6 +174,7 @@ namespace C4IT.LIAM
Path = RootPath, Path = RootPath,
Level = 0 Level = 0
}); });
await NtfsRootFolder.ResolvePermissionGroupsAsync(NtfsRootFolder.TechnicalName);
DataAreas.Add(NtfsRootFolder); DataAreas.Add(NtfsRootFolder);
break; break;
} }
@@ -189,6 +191,7 @@ namespace C4IT.LIAM
var Folder = new cLiamNtfsFolder(this, share, NtfsRootFolder, (cNtfsResultFolder)Entry.Value); var Folder = new cLiamNtfsFolder(this, share, NtfsRootFolder, (cNtfsResultFolder)Entry.Value);
await Folder.ResolvePermissionGroupsAsync(Folder.TechnicalName);
DataAreas.Add(Folder); DataAreas.Add(Folder);
} }
return DataAreas; return DataAreas;
@@ -217,6 +220,8 @@ namespace C4IT.LIAM
LogEntry($"Error: License not valid", LogLevels.Error); LogEntry($"Error: License not valid", LogLevels.Error);
return null; return null;
} }
if (!await LogonAsync())
return null;
var splt = UID.Split(System.IO.Path.DirectorySeparatorChar); var splt = UID.Split(System.IO.Path.DirectorySeparatorChar);
var name = Path.GetDirectoryName(UID); var name = Path.GetDirectoryName(UID);
switch (splt.Length) switch (splt.Length)
@@ -226,21 +231,25 @@ namespace C4IT.LIAM
return null; return null;
case 2: case 2:
{ {
return new cLiamNtfsShare(this, new cNtfsResultShare() var share = new cLiamNtfsShare(this, new cNtfsResultShare()
{ {
DisplayName = name, DisplayName = name,
Path = UID, Path = UID,
Level = getDepth(UID) Level = getDepth(UID)
}); });
await share.ResolvePermissionGroupsAsync(share.TechnicalName);
return share;
} }
default: default:
{ {
return new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder() var folder = new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder()
{ {
DisplayName = name, DisplayName = name,
Path = UID, Path = UID,
Level = getDepth(UID) Level = getDepth(UID)
}); });
await folder.ResolvePermissionGroupsAsync(folder.TechnicalName);
return folder;
} }
} }
} }
@@ -365,16 +374,182 @@ namespace C4IT.LIAM
} }
public class cLiamNtfsShare : cLiamDataAreaBase public abstract class cLiamNtfsPermissionDataAreaBase : cLiamDataAreaBase
{ {
public new readonly cLiamProviderNtfs Provider = null; 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; private readonly cNtfsResultBase Share = null;
public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share) : public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share) :
base(Provider) base(Provider)
{ {
this.Provider = Provider;
this.Share = Share; this.Share = Share;
this.DisplayName = Share.Path.Split('\\').Last(); this.DisplayName = Share.Path.Split('\\').Last();
@@ -382,8 +557,8 @@ namespace C4IT.LIAM
this.UID = cLiamNtfsFolder.GetUniqueDataAreaID(Share.Path); this.UID = cLiamNtfsFolder.GetUniqueDataAreaID(Share.Path);
this.Level = Share.Level; this.Level = Share.Level;
this.DataType = eLiamDataAreaTypes.NtfsShare; this.DataType = eLiamDataAreaTypes.NtfsShare;
this.SupportsOwners = false; if (Directory.Exists(Share.Path))
this.SupportsPermissions = false; this.CreatedDate = new DirectoryInfo(Share.Path).CreationTimeUtc.ToString("s");
} }
internal async Task<List<cLiamDataAreaBase>> getFolders() internal async Task<List<cLiamDataAreaBase>> getFolders()
@@ -447,19 +622,13 @@ namespace C4IT.LIAM
this.scope = secGroup.Scope.ToString(); this.scope = secGroup.Scope.ToString();
} }
} }
public class cLiamNtfsFolder : cLiamDataAreaBase public class cLiamNtfsFolder : cLiamNtfsPermissionDataAreaBase
{ {
public new readonly cLiamProviderNtfs Provider = null;
public readonly cLiamNtfsShare Share = null; public readonly cLiamNtfsShare Share = null;
public readonly cLiamNtfsFolder NtfsRootFolder = null; public readonly cLiamNtfsFolder NtfsRootFolder = 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";
public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder) : base(Provider) public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder) : base(Provider)
{ {
var ntfsParent = NtfsFolder.Parent; var ntfsParent = NtfsFolder.Parent;
this.Provider = Provider;
this.NtfsRootFolder = ntfsRootFolder; this.NtfsRootFolder = ntfsRootFolder;
this.Share = share; this.Share = share;
this.TechnicalName = NtfsFolder.Path; this.TechnicalName = NtfsFolder.Path;
@@ -467,7 +636,6 @@ namespace C4IT.LIAM
this.DisplayName = new DirectoryInfo(NtfsFolder.Path).Name; this.DisplayName = new DirectoryInfo(NtfsFolder.Path).Name;
this.Level = NtfsFolder.Level; this.Level = NtfsFolder.Level;
this.DataType = eLiamDataAreaTypes.NtfsFolder; this.DataType = eLiamDataAreaTypes.NtfsFolder;
this.SupportsPermissions = true;
this.CreatedDate = NtfsFolder.CreatedDate; this.CreatedDate = NtfsFolder.CreatedDate;
if (ntfsParent != null) if (ntfsParent != null)
{ {
@@ -477,155 +645,6 @@ namespace C4IT.LIAM
{ {
this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath); this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath);
} }
assignPermissionGroups(Provider, NtfsFolder);
}
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);
}
}
private 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 folder '{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 folder '{this.TechnicalName}'", LogLevels.Warning);
return null;
}
var RetVal = new List<cLiamUserInfo>(lstMembers.Count);
LogEntry($"Owners for folder 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);
}
}
private async void assignPermissionGroups(cLiamProviderNtfs Provider, cNtfsResultFolder NtfsFolder)
{
var ACLs = Provider.activeDirectoryBase.GetAccessControlList(NtfsFolder.Path);
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)
{
//skip ACLs for user "everyone"
if (rule.IdentityReference.Value == "S-1-1-0")
continue;
GroupPrincipal grp = GroupPrincipal.FindByIdentity(Provider.activeDirectoryBase.adContext, IdentityType.Sid, rule.IdentityReference.Value);
if (grp != null)
{
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))
{
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))
{
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))
{
TraverseGroupIdentifier = rule.IdentityReference.Value;
}
else
DefaultLogger.LogEntry(LogLevels.Debug, $"No match for: {grp.Name}");
}
}
} }
public static string GetUniqueDataAreaID(string fullPath) public static string GetUniqueDataAreaID(string fullPath)

View File

@@ -524,7 +524,7 @@ namespace LiamWorkflowActivities
return lstDataAreas return lstDataAreas
.Select(DataArea => .Select(DataArea =>
{ {
var ntfs = DataArea as cLiamNtfsFolder; var ntfsPermissionArea = DataArea as cLiamNtfsPermissionDataAreaBase;
var adGrp = DataArea as cLiamAdGroupAsDataArea; var adGrp = DataArea as cLiamAdGroupAsDataArea;
var exchMB = DataArea as cLiamExchangeSharedMailbox; var exchMB = DataArea as cLiamExchangeSharedMailbox;
var exchDL = DataArea as cLiamExchangeDistributionGroup; var exchDL = DataArea as cLiamExchangeDistributionGroup;
@@ -537,7 +537,7 @@ namespace LiamWorkflowActivities
string owner = exchMB?.OwnerGroupIdentifier string owner = exchMB?.OwnerGroupIdentifier
?? exchDL?.OwnerGroupIdentifier ?? exchDL?.OwnerGroupIdentifier
?? adGrp?.ManagedBySID ?? adGrp?.ManagedBySID
?? ntfs?.OwnerGroupIdentifier ?? ntfsPermissionArea?.OwnerGroupIdentifier
?? string.Empty; ?? string.Empty;
// 2) WriteSID // 2) WriteSID
@@ -548,9 +548,9 @@ namespace LiamWorkflowActivities
string write = exchMB != null string write = exchMB != null
? exchMB.FullAccessGroupSid ? exchMB.FullAccessGroupSid
: exchDL != null : exchDL != null
? exchDL.MemberGroupSid ? exchDL.MemberGroupSid
: adGrp?.UID : adGrp?.UID
?? ntfs?.WriteGroupIdentifier ?? ntfsPermissionArea?.WriteGroupIdentifier
?? string.Empty; ?? string.Empty;
// 3) ReadSID // 3) ReadSID
@@ -559,14 +559,14 @@ namespace LiamWorkflowActivities
// - NTFS-Folder: ReadGroupIdentifier // - NTFS-Folder: ReadGroupIdentifier
string read = exchMB != null string read = exchMB != null
? exchMB.SendAsGroupSid ? exchMB.SendAsGroupSid
: ntfs?.ReadGroupIdentifier : ntfsPermissionArea?.ReadGroupIdentifier
?? string.Empty; ?? string.Empty;
// 4) Traverse nur NTFS-Folder // 4) Traverse nur NTFS-Objekte
string traverse = ntfs?.TraverseGroupIdentifier ?? string.Empty; string traverse = ntfsPermissionArea?.TraverseGroupIdentifier ?? string.Empty;
// 5) CreatedDate nur NTFS-Folder // 5) CreatedDate nur NTFS-Objekte
string created = ntfs?.CreatedDate ?? DateTime.MinValue.ToString("o"); string created = ntfsPermissionArea?.CreatedDate ?? DateTime.MinValue.ToString("o");
// 6) Description: nur AD-Group // 6) Description: nur AD-Group
string desc = adGrp?.Description ?? string.Empty; string desc = adGrp?.Description ?? string.Empty;