feat: ensure missing NTFS permission groups
This commit is contained in:
@@ -311,7 +311,7 @@ namespace C4IT.LIAM
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ResultToken> CreateDataAreaAsync(
|
public Task<ResultToken> CreateDataAreaAsync(
|
||||||
string newFolderPath,
|
string newFolderPath,
|
||||||
string newFolderParent,
|
string newFolderParent,
|
||||||
IDictionary<string, string> customTags,
|
IDictionary<string, string> customTags,
|
||||||
@@ -320,27 +320,157 @@ namespace C4IT.LIAM
|
|||||||
IEnumerable<string> writerSids
|
IEnumerable<string> writerSids
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// 1) Instanziere DataArea_FileSystem und fülle Konfiguration:
|
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 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
|
var engine = new DataArea_FileSystem
|
||||||
{
|
{
|
||||||
|
ConfigID = "manual",
|
||||||
domainName = this.Domain,
|
domainName = this.Domain,
|
||||||
username = this.Credential.Identification,
|
username = this.Credential.Identification,
|
||||||
password = new NetworkCredential("", this.Credential.Secret).SecurePassword,
|
password = new NetworkCredential("", this.Credential.Secret).SecurePassword,
|
||||||
baseFolder = this.RootPath,
|
baseFolder = this.RootPath,
|
||||||
newFolderPath = newFolderPath,
|
newFolderPath = folderPath,
|
||||||
newFolderParent = newFolderParent,
|
newFolderParent = parentFolderPath,
|
||||||
groupPrefix = CustomTags["Filesystem_GroupPrefixTag"],
|
groupPrefix = GetRequiredCustomTag("Filesystem_GroupPrefixTag"),
|
||||||
groupOUPath = this.GroupPath,
|
groupOUPath = this.GroupPath,
|
||||||
groupPermissionStrategy = (C4IT_IAM_GET.PermissionGroupStrategy)this.GroupStrategy,
|
groupPermissionStrategy = (C4IT_IAM_GET.PermissionGroupStrategy)this.GroupStrategy,
|
||||||
groupCustomTags = customTags,
|
groupCustomTags = mergedCustomTags,
|
||||||
ownerUserSids = ownerSids?.ToList(),
|
ownerUserSids = ownerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
||||||
readerUserSids = readerSids?.ToList(),
|
readerUserSids = readerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
||||||
writerUserSids = writerSids?.ToList(),
|
writerUserSids = writerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
||||||
// Templates aus NamingConventions übernehmen…
|
groupOwnerTag = GetRequiredCustomTag("Filesystem_GroupOwnerTag"),
|
||||||
|
groupWriteTag = GetRequiredCustomTag("Filesystem_GroupWriteTag"),
|
||||||
|
groupReadTag = GetRequiredCustomTag("Filesystem_GroupReadTag"),
|
||||||
|
groupTraverseTag = GetRequiredCustomTag("Filesystem_GroupTraverseTag"),
|
||||||
|
groupDLTag = GetRequiredCustomTag("Filesystem_GroupDomainLocalTag"),
|
||||||
|
groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag")
|
||||||
};
|
};
|
||||||
// 2) Engine starten
|
|
||||||
var result = engine.createDataArea();
|
foreach (var template in BuildSecurityGroupTemplates())
|
||||||
return result;
|
engine.templates.Add(template);
|
||||||
|
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
throw new InvalidOperationException($"Missing NTFS custom tag '{key}'.");
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -225,6 +225,134 @@ namespace C4IT_IAM_SET
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResultToken checkRequiredVariablesForEnsure()
|
||||||
|
{
|
||||||
|
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
|
||||||
|
resultToken.resultErrorId = 0;
|
||||||
|
if (String.IsNullOrEmpty(ConfigID))
|
||||||
|
{
|
||||||
|
resultToken.resultErrorId = 30001;
|
||||||
|
resultToken.resultMessage = "Kein ConfigID gewählt ";
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(username) | String.IsNullOrEmpty(new NetworkCredential("", password).Password) | String.IsNullOrEmpty(domainName))
|
||||||
|
{
|
||||||
|
resultToken.resultErrorId = 30002;
|
||||||
|
resultToken.resultMessage = "Fehlende Anmeldeinformationen";
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
if (String.IsNullOrEmpty(groupPrefix))
|
||||||
|
{
|
||||||
|
resultToken.resultErrorId = 30004;
|
||||||
|
resultToken.resultMessage = "Kein Gruppen Präfix angegeben";
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
if (String.IsNullOrEmpty(newFolderPath))
|
||||||
|
{
|
||||||
|
resultToken.resultErrorId = 30005;
|
||||||
|
resultToken.resultMessage = "Kein Pfad für Verzeichnis angegeben";
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
if (String.IsNullOrEmpty(baseFolder))
|
||||||
|
{
|
||||||
|
resultToken.resultErrorId = 30007;
|
||||||
|
resultToken.resultMessage = "Kein Basisverzeichnis angegeben";
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeFolderContext()
|
||||||
|
{
|
||||||
|
newDataArea = new DataArea();
|
||||||
|
var folder = new IAM_Folder
|
||||||
|
{
|
||||||
|
configurationID = ConfigID,
|
||||||
|
technicalName = newFolderPath,
|
||||||
|
targetType = (int)IAM_TargetType.FileSystem,
|
||||||
|
Parent = newFolderParent,
|
||||||
|
ParentUID = string.IsNullOrWhiteSpace(newFolderParent) ? string.Empty : DataArea.GetUniqueDataAreaID(newFolderParent),
|
||||||
|
baseFolder = baseFolder
|
||||||
|
};
|
||||||
|
newDataArea.IAM_Folders.Add(folder);
|
||||||
|
|
||||||
|
newSecurityGroups = new SecurityGroups
|
||||||
|
{
|
||||||
|
username = username,
|
||||||
|
domainName = domainName,
|
||||||
|
password = password
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultToken ensureDataAreaPermissions(bool ensureTraverseGroups = false)
|
||||||
|
{
|
||||||
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var resultToken = checkRequiredVariablesForEnsure();
|
||||||
|
if (resultToken.resultErrorId != 0)
|
||||||
|
return resultToken;
|
||||||
|
|
||||||
|
if (Connection != null)
|
||||||
|
Connection.Dispose();
|
||||||
|
|
||||||
|
using (Connection = new cNetworkConnection(baseFolder, username, new NetworkCredential("", password).Password))
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(newFolderPath))
|
||||||
|
{
|
||||||
|
resultToken.resultErrorId = 30203;
|
||||||
|
resultToken.resultMessage = "Verzeichnis existiert nicht";
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentDirectory = Directory.GetParent(newFolderPath);
|
||||||
|
if (string.IsNullOrWhiteSpace(newFolderParent))
|
||||||
|
newFolderParent = parentDirectory?.FullName;
|
||||||
|
|
||||||
|
InitializeFolderContext();
|
||||||
|
|
||||||
|
ensureADGroups(resultToken);
|
||||||
|
resultToken = ensureFolderPermissions(resultToken);
|
||||||
|
|
||||||
|
if (resultToken.resultErrorId != 0)
|
||||||
|
return resultToken;
|
||||||
|
|
||||||
|
if (ensureTraverseGroups)
|
||||||
|
{
|
||||||
|
var traverseResult = SetTraversePermissions();
|
||||||
|
if (traverseResult != null)
|
||||||
|
{
|
||||||
|
resultToken.createdGroups.AddRange(traverseResult.createdGroups);
|
||||||
|
resultToken.reusedGroups.AddRange(traverseResult.reusedGroups);
|
||||||
|
resultToken.addedAclEntries.AddRange(traverseResult.addedAclEntries);
|
||||||
|
resultToken.skippedAclEntries.AddRange(traverseResult.skippedAclEntries);
|
||||||
|
resultToken.ensuredTraverseGroups.AddRange(traverseResult.ensuredTraverseGroups);
|
||||||
|
resultToken.warnings.AddRange(traverseResult.warnings);
|
||||||
|
if (traverseResult.resultErrorId != 0)
|
||||||
|
{
|
||||||
|
resultToken.resultErrorId = traverseResult.resultErrorId;
|
||||||
|
resultToken.resultMessage = traverseResult.resultMessage;
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultToken.resultMessage = "Gruppen und ACLs erfolgreich sichergestellt";
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception E)
|
||||||
|
{
|
||||||
|
cLogManager.DefaultLogger.LogException(E);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ResultToken SetTraversePermissions()
|
private ResultToken SetTraversePermissions()
|
||||||
{
|
{
|
||||||
LogMethodBegin(MethodBase.GetCurrentMethod());
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||||
@@ -350,6 +478,7 @@ namespace C4IT_IAM_SET
|
|||||||
}
|
}
|
||||||
|
|
||||||
GroupPrincipal parentTraverseGroup = null;
|
GroupPrincipal parentTraverseGroup = null;
|
||||||
|
var parentTraverseAclExists = false;
|
||||||
string relativePathRaw = DataArea.GetRelativePath(parent.FullName, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
string relativePathRaw = DataArea.GetRelativePath(parent.FullName, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||||
relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||||
DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath vor Normalisierung: {relativePathRaw}");
|
DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath vor Normalisierung: {relativePathRaw}");
|
||||||
@@ -361,6 +490,8 @@ namespace C4IT_IAM_SET
|
|||||||
var folderName = sanitizedSegments.Length > 0
|
var folderName = sanitizedSegments.Length > 0
|
||||||
? sanitizedSegments[sanitizedSegments.Length - 1]
|
? sanitizedSegments[sanitizedSegments.Length - 1]
|
||||||
: Helper.SanitizePathSegment(Path.GetFileName(parent.FullName.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)));
|
: Helper.SanitizePathSegment(Path.GetFileName(parent.FullName.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)));
|
||||||
|
var traverseNameTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, relativePath, sanitizedSegments, folderName);
|
||||||
|
var traverseDescriptionTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, relativePath, sanitizedSegments, folderName);
|
||||||
|
|
||||||
string traverseRegex = null;
|
string traverseRegex = null;
|
||||||
try
|
try
|
||||||
@@ -397,12 +528,29 @@ namespace C4IT_IAM_SET
|
|||||||
if (princ != null && Regex.IsMatch(princ.Name, traverseRegex, RegexOptions.IgnoreCase))
|
if (princ != null && Regex.IsMatch(princ.Name, traverseRegex, RegexOptions.IgnoreCase))
|
||||||
{
|
{
|
||||||
parentTraverseGroup = princ;
|
parentTraverseGroup = princ;
|
||||||
|
parentTraverseAclExists = true;
|
||||||
DefaultLogger.LogEntry(LogLevels.Debug, $"parentTraverseGroup gesetzt: {parentTraverseGroup.Name}");
|
DefaultLogger.LogEntry(LogLevels.Debug, $"parentTraverseGroup gesetzt: {parentTraverseGroup.Name}");
|
||||||
}
|
}
|
||||||
if (parentTraverseGroup != null)
|
if (parentTraverseGroup != null)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parentTraverseGroup == null && !string.IsNullOrEmpty(traverseNameTemplate))
|
||||||
|
{
|
||||||
|
for (var loop = 0; loop < 20; loop++)
|
||||||
|
{
|
||||||
|
var candidateName = traverseNameTemplate.ReplaceLoopTag(loop);
|
||||||
|
parentTraverseGroup = GroupPrincipal.FindByIdentity(domainContext, candidateName);
|
||||||
|
if (parentTraverseGroup == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
resultToken.reusedGroups.Add(candidateName);
|
||||||
|
resultToken.ensuredTraverseGroups.Add(candidateName);
|
||||||
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Vorhandene Traverse-Gruppe wiederverwendet: {candidateName}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parentTraverseGroup == null && !traverseGroupTemplate.NamingTemplate.Equals(string.Empty))
|
if (parentTraverseGroup == null && !traverseGroupTemplate.NamingTemplate.Equals(string.Empty))
|
||||||
{
|
{
|
||||||
DefaultLogger.LogEntry(LogLevels.Debug, "Erstelle neue TraverseGroup.");
|
DefaultLogger.LogEntry(LogLevels.Debug, "Erstelle neue TraverseGroup.");
|
||||||
@@ -413,8 +561,6 @@ namespace C4IT_IAM_SET
|
|||||||
}
|
}
|
||||||
|
|
||||||
IAM_SecurityGroup newTraverseGroup = null;
|
IAM_SecurityGroup newTraverseGroup = null;
|
||||||
var traverseNameTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, relativePath, sanitizedSegments, folderName);
|
|
||||||
var traverseDescriptionTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, relativePath, sanitizedSegments, folderName);
|
|
||||||
var loop = 0;
|
var loop = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -452,6 +598,8 @@ namespace C4IT_IAM_SET
|
|||||||
{
|
{
|
||||||
newSecurityGroups.CreateADGroup(groupOUPath, newTraverseGroup, null);
|
newSecurityGroups.CreateADGroup(groupOUPath, newTraverseGroup, null);
|
||||||
DefaultLogger.LogEntry(LogLevels.Debug, $"AD-Gruppe erstellt: {newTraverseGroup.Name}");
|
DefaultLogger.LogEntry(LogLevels.Debug, $"AD-Gruppe erstellt: {newTraverseGroup.Name}");
|
||||||
|
resultToken.createdGroups.Add(newTraverseGroup.Name);
|
||||||
|
resultToken.ensuredTraverseGroups.Add(newTraverseGroup.Name);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -474,6 +622,8 @@ namespace C4IT_IAM_SET
|
|||||||
AccessControlType.Allow));
|
AccessControlType.Allow));
|
||||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Setze Traverse-ACL auf: {parent.FullName} für {parentTraverseGroup.DistinguishedName}");
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Setze Traverse-ACL auf: {parent.FullName} für {parentTraverseGroup.DistinguishedName}");
|
||||||
parent.SetAccessControl(accesscontrol);
|
parent.SetAccessControl(accesscontrol);
|
||||||
|
resultToken.addedAclEntries.Add(parentTraverseGroup.Name);
|
||||||
|
parentTraverseAclExists = true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -488,6 +638,37 @@ namespace C4IT_IAM_SET
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parentTraverseGroup != null && !parentTraverseAclExists)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var accessControl = parent.GetAccessControl();
|
||||||
|
var rules = accessControl.GetAccessRules(true, true, typeof(SecurityIdentifier)).Cast<FileSystemAccessRule>();
|
||||||
|
var hasAcl = rules.Any(rule =>
|
||||||
|
rule.AccessControlType == AccessControlType.Allow
|
||||||
|
&& rule.IdentityReference.Value == parentTraverseGroup.Sid.Value
|
||||||
|
&& (rule.FileSystemRights & FileSystemRights.Read) == FileSystemRights.Read);
|
||||||
|
|
||||||
|
if (hasAcl)
|
||||||
|
{
|
||||||
|
resultToken.skippedAclEntries.Add(parentTraverseGroup.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
accessControl.AddAccessRule(new FileSystemAccessRule(parentTraverseGroup.Sid,
|
||||||
|
FileSystemRights.Read, InheritanceFlags.None, PropagationFlags.None,
|
||||||
|
AccessControlType.Allow));
|
||||||
|
parent.SetAccessControl(accessControl);
|
||||||
|
resultToken.addedAclEntries.Add(parentTraverseGroup.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Sicherstellen der Traverse-ACL: {ex.Message}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parentTraverseGroup != null)
|
if (parentTraverseGroup != null)
|
||||||
{
|
{
|
||||||
if (i == lvl)
|
if (i == lvl)
|
||||||
@@ -566,6 +747,9 @@ namespace C4IT_IAM_SET
|
|||||||
DefaultLogger.LogEntry(LogLevels.Debug, "parentTraverseGroup ist null.");
|
DefaultLogger.LogEntry(LogLevels.Debug, "parentTraverseGroup ist null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parentTraverseGroup != null && !resultToken.ensuredTraverseGroups.Contains(parentTraverseGroup.Name))
|
||||||
|
resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name);
|
||||||
|
|
||||||
// Aktualisiere parent und lvl für die nächste Iteration
|
// Aktualisiere parent und lvl für die nächste Iteration
|
||||||
parent = parent.Parent;
|
parent = parent.Parent;
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
@@ -620,6 +804,136 @@ namespace C4IT_IAM_SET
|
|||||||
LogMethodEnd(MethodBase.GetCurrentMethod());
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasMatchingAccessRule(DirectoryInfo directory, SecurityIdentifier sid, FileSystemRights rights)
|
||||||
|
{
|
||||||
|
var rules = directory.GetAccessControl(AccessControlSections.Access)
|
||||||
|
.GetAccessRules(true, true, typeof(SecurityIdentifier))
|
||||||
|
.Cast<FileSystemAccessRule>();
|
||||||
|
|
||||||
|
foreach (var rule in rules)
|
||||||
|
{
|
||||||
|
if (rule.AccessControlType != AccessControlType.Allow)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!(rule.IdentityReference is SecurityIdentifier ruleSid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!string.Equals(ruleSid.Value, sid.Value, StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((rule.FileSystemRights & rights) == rights)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultToken ensureFolderPermissions(ResultToken resultToken)
|
||||||
|
{
|
||||||
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var directory = new DirectoryInfo(newDataArea.IAM_Folders[0].technicalName);
|
||||||
|
foreach (var currentSecGroup in newSecurityGroups.IAM_SecurityGroups)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(currentSecGroup?.UID))
|
||||||
|
{
|
||||||
|
resultToken.warnings.Add($"Keine SID für Gruppe '{currentSecGroup?.Name}' verfügbar.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(groupPermissionStrategy == PermissionGroupStrategy.AGDLP && currentSecGroup.Scope == GroupScope.Local
|
||||||
|
|| groupPermissionStrategy == PermissionGroupStrategy.AGP && currentSecGroup.Scope == GroupScope.Global))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sid = new SecurityIdentifier(currentSecGroup.UID);
|
||||||
|
if (HasMatchingAccessRule(directory, sid, currentSecGroup.rights))
|
||||||
|
{
|
||||||
|
resultToken.skippedAclEntries.Add(currentSecGroup.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataArea.AddDirectorySecurity(newDataArea.IAM_Folders[0].baseFolder, newDataArea.IAM_Folders[0].technicalName, sid, currentSecGroup.rights, AccessControlType.Allow);
|
||||||
|
resultToken.addedAclEntries.Add(currentSecGroup.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultToken;
|
||||||
|
}
|
||||||
|
catch (Exception E)
|
||||||
|
{
|
||||||
|
cLogManager.DefaultLogger.LogException(E);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureADGroups(ResultToken resultToken)
|
||||||
|
{
|
||||||
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newSecurityGroups.IAM_SecurityGroups.Clear();
|
||||||
|
newSecurityGroups.GenerateNewSecurityGroups(baseFolder,
|
||||||
|
newDataArea.IAM_Folders[0].technicalName,
|
||||||
|
groupPrefix,
|
||||||
|
groupOUPath,
|
||||||
|
groupPermissionStrategy,
|
||||||
|
groupTraverseTag,
|
||||||
|
groupReadTag,
|
||||||
|
groupWriteTag,
|
||||||
|
groupOwnerTag,
|
||||||
|
groupDLTag,
|
||||||
|
groupGTag,
|
||||||
|
groupCustomTags,
|
||||||
|
templates,
|
||||||
|
ReadACLPermission,
|
||||||
|
WriteACLPermission,
|
||||||
|
OwnerACLPermission,
|
||||||
|
0);
|
||||||
|
|
||||||
|
List<UserPrincipal> owners = getUserPrincipalBySid(ownerUserSids);
|
||||||
|
List<UserPrincipal> writers = getUserPrincipalBySid(writerUserSids);
|
||||||
|
List<UserPrincipal> readers = getUserPrincipalBySid(readerUserSids);
|
||||||
|
|
||||||
|
for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
|
||||||
|
{
|
||||||
|
List<UserPrincipal> users;
|
||||||
|
if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupOwnerTag.ToUpper()))
|
||||||
|
users = owners;
|
||||||
|
else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupWriteTag.ToUpper()))
|
||||||
|
users = writers;
|
||||||
|
else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupReadTag.ToUpper()))
|
||||||
|
users = readers;
|
||||||
|
else
|
||||||
|
users = null;
|
||||||
|
|
||||||
|
var groupAlreadyExists = newSecurityGroups.GroupAllreadyExisting(newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper());
|
||||||
|
newSecurityGroups.EnsureADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], users);
|
||||||
|
if (groupAlreadyExists)
|
||||||
|
resultToken.reusedGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
|
||||||
|
else
|
||||||
|
resultToken.createdGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception E)
|
||||||
|
{
|
||||||
|
cLogManager.DefaultLogger.LogException(E);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ResultToken createFolder()
|
private ResultToken createFolder()
|
||||||
{
|
{
|
||||||
LogMethodBegin(MethodBase.GetCurrentMethod());
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ namespace C4IT_IAM_Engine
|
|||||||
public string resultMessage;
|
public string resultMessage;
|
||||||
public int resultErrorId;
|
public int resultErrorId;
|
||||||
public string resultFunction;
|
public string resultFunction;
|
||||||
|
public List<string> createdGroups = new List<string>();
|
||||||
|
public List<string> reusedGroups = new List<string>();
|
||||||
|
public List<string> addedAclEntries = new List<string>();
|
||||||
|
public List<string> skippedAclEntries = new List<string>();
|
||||||
|
public List<string> ensuredTraverseGroups = new List<string>();
|
||||||
|
public List<string> warnings = new List<string>();
|
||||||
public ResultToken(string function)
|
public ResultToken(string function)
|
||||||
{
|
{
|
||||||
this.resultFunction = function;
|
this.resultFunction = function;
|
||||||
|
|||||||
@@ -361,6 +361,108 @@ namespace C4IT_IAM_Engine
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DirectoryEntry FindGroupEntry(string groupName)
|
||||||
|
{
|
||||||
|
DirectoryEntry entry = new DirectoryEntry
|
||||||
|
{
|
||||||
|
Path = "LDAP://" + domainName,
|
||||||
|
Username = username,
|
||||||
|
Password = new NetworkCredential("", password).Password,
|
||||||
|
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
|
||||||
|
};
|
||||||
|
|
||||||
|
DirectorySearcher search = new DirectorySearcher(entry)
|
||||||
|
{
|
||||||
|
Filter = "(&(objectClass=group)(sAMAccountName=" + groupName.ToUpper() + "))"
|
||||||
|
};
|
||||||
|
search.PageSize = 100000;
|
||||||
|
|
||||||
|
var result = search.FindOne();
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
entry.Dispose();
|
||||||
|
search.Dispose();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupEntry = result.GetDirectoryEntry();
|
||||||
|
search.Dispose();
|
||||||
|
entry.Dispose();
|
||||||
|
return groupEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasMember(PropertyValueCollection members, string distinguishedName)
|
||||||
|
{
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
if (string.Equals(member?.ToString(), distinguishedName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddMissingMembers(DirectoryEntry group, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
||||||
|
{
|
||||||
|
if (group == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
|
if (users != null && secGroup.Scope == GroupScope.Global)
|
||||||
|
{
|
||||||
|
foreach (var user in users.Where(u => u != null && !string.IsNullOrWhiteSpace(u.DistinguishedName)))
|
||||||
|
{
|
||||||
|
if (HasMember(group.Properties["member"], user.DistinguishedName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing member: {user.DistinguishedName}");
|
||||||
|
group.Properties["member"].Add(user.DistinguishedName);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secGroup.Scope == GroupScope.Local)
|
||||||
|
{
|
||||||
|
foreach (var innerGroup in secGroup.memberGroups.Where(g => g != null && !string.IsNullOrWhiteSpace(g.technicalName)))
|
||||||
|
{
|
||||||
|
if (HasMember(group.Properties["member"], innerGroup.technicalName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing nested group: {innerGroup.technicalName}");
|
||||||
|
group.Properties["member"].Add(innerGroup.technicalName);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
group.CommitChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryEntry EnsureADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
||||||
|
{
|
||||||
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var existingGroup = FindGroupEntry(secGroup.Name);
|
||||||
|
if (existingGroup == null)
|
||||||
|
return CreateADGroup(ouPath, secGroup, users);
|
||||||
|
|
||||||
|
AddMissingMembers(existingGroup, secGroup, users);
|
||||||
|
var objectid = getSID(existingGroup);
|
||||||
|
secGroup.UID = objectid;
|
||||||
|
return existingGroup;
|
||||||
|
}
|
||||||
|
catch (Exception E)
|
||||||
|
{
|
||||||
|
cLogManager.DefaultLogger.LogException(E);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public DirectoryEntry CreateADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
public DirectoryEntry CreateADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
||||||
{
|
{
|
||||||
LogMethodBegin(MethodBase.GetCurrentMethod());
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||||
@@ -406,9 +508,13 @@ namespace C4IT_IAM_Engine
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DirectoryEntry e = new DirectoryEntry("LDAP://" + domainName + "/" + "CN =" + secGroup.Name.ToUpper() + "," + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
DirectoryEntry e = FindGroupEntry(secGroup.Name);
|
||||||
|
if (e == null)
|
||||||
|
return null;
|
||||||
var objectid = getSID(e);
|
var objectid = getSID(e);
|
||||||
secGroup.UID = objectid;
|
secGroup.UID = objectid;
|
||||||
|
AddMissingMembers(e, secGroup, users);
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using static LiamAD.ADServiceGroupCreator;
|
using static LiamAD.ADServiceGroupCreator;
|
||||||
using C4IT.LIAM;
|
using C4IT.LIAM;
|
||||||
|
using C4IT_IAM_Engine;
|
||||||
|
|
||||||
namespace C4IT.LIAM.Activities
|
namespace C4IT.LIAM.Activities
|
||||||
{
|
{
|
||||||
@@ -959,5 +960,107 @@ namespace C4IT.LIAM.Activities
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class C4ITLIAMEnsureNtfsPermissionGroupsActivity : cLIAMM42BaseActivity
|
||||||
|
{
|
||||||
|
[Category("Input")]
|
||||||
|
[DisplayName("Config Id")]
|
||||||
|
[RequiredArgument]
|
||||||
|
public InArgument<Guid> ConfigID { get; set; }
|
||||||
|
|
||||||
|
[Category("Input")]
|
||||||
|
[DisplayName("Folder Path")]
|
||||||
|
[RequiredArgument]
|
||||||
|
public InArgument<string> FolderPath { get; set; }
|
||||||
|
|
||||||
|
[Category("Input")]
|
||||||
|
[DisplayName("Owner SIDs")]
|
||||||
|
public InArgument<string> OwnerSids { get; set; }
|
||||||
|
|
||||||
|
[Category("Input")]
|
||||||
|
[DisplayName("Reader SIDs")]
|
||||||
|
public InArgument<string> ReaderSids { get; set; }
|
||||||
|
|
||||||
|
[Category("Input")]
|
||||||
|
[DisplayName("Writer SIDs")]
|
||||||
|
public InArgument<string> WriterSids { get; set; }
|
||||||
|
|
||||||
|
[Category("Input")]
|
||||||
|
[DisplayName("Ensure Traverse")]
|
||||||
|
public InArgument<bool> EnsureTraverse { get; set; }
|
||||||
|
|
||||||
|
[Category("Output")]
|
||||||
|
[DisplayName("Success")]
|
||||||
|
public OutArgument<bool> Success { get; set; }
|
||||||
|
|
||||||
|
[Category("Output")]
|
||||||
|
[DisplayName("Result")]
|
||||||
|
public OutArgument<JsonValue> ResultToken { get; set; }
|
||||||
|
|
||||||
|
protected override void Execute(NativeActivityContext context)
|
||||||
|
{
|
||||||
|
EnsureDataProviders(context);
|
||||||
|
|
||||||
|
var cfgId = ConfigID.Get(context);
|
||||||
|
var providerEntry = getDataProvider(cfgId);
|
||||||
|
var provider = providerEntry?.Provider as cLiamProviderNtfs;
|
||||||
|
var folderPath = FolderPath.Get(context);
|
||||||
|
if (provider == null || string.IsNullOrWhiteSpace(folderPath))
|
||||||
|
{
|
||||||
|
Success.Set(context, false);
|
||||||
|
ResultToken.Set(context, JsonValue.Parse(JsonConvert.SerializeObject(new ResultToken(GetType().Name)
|
||||||
|
{
|
||||||
|
resultErrorId = 1,
|
||||||
|
resultMessage = provider == null ? "Configured provider is not NTFS or not initialized." : "Folder path is missing."
|
||||||
|
})));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = provider.EnsureMissingPermissionGroupsAsync(
|
||||||
|
folderPath,
|
||||||
|
null,
|
||||||
|
ParseSidList(OwnerSids.Get(context)),
|
||||||
|
ParseSidList(ReaderSids.Get(context)),
|
||||||
|
ParseSidList(WriterSids.Get(context)),
|
||||||
|
EnsureTraverse.Get(context)).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
Success.Set(context, result != null && result.resultErrorId == 0);
|
||||||
|
ResultToken.Set(context, JsonValue.Parse(JsonConvert.SerializeObject(result)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> ParseSidList(string raw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(raw))
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
var trimmed = raw.Trim();
|
||||||
|
if (trimmed.StartsWith("["))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<List<string>>(trimmed) ?? Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed
|
||||||
|
.Split(new[] { ';', ',', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(i => i.Trim())
|
||||||
|
.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureDataProviders(NativeActivityContext context)
|
||||||
|
{
|
||||||
|
if (executor == null)
|
||||||
|
executor = context.GetExtension<IExtensionExecutor>();
|
||||||
|
if (schemaReader == null)
|
||||||
|
schemaReader = executor.Get<ISchemaReaderProvider>();
|
||||||
|
if (dataProvider == null)
|
||||||
|
dataProvider = executor.Get<IDataReaderProvider>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,4 +52,19 @@ namespace C4IT.LIAM.Activities.Design.Metadata
|
|||||||
builder.AddCustomAttributes(base.Activity, "CreatedTeamId", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.Out));
|
builder.AddCustomAttributes(base.Activity, "CreatedTeamId", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.Out));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class C4ITLIAMEnsureNtfsPermissionGroupsActivityMetadata : ActivityMetadata<C4ITLIAMEnsureNtfsPermissionGroupsActivity>
|
||||||
|
{
|
||||||
|
public override void RegisterMetadata(AttributeTableBuilder builder)
|
||||||
|
{
|
||||||
|
builder.AddCustomAttributes(base.Activity, "ConfigID", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.In));
|
||||||
|
builder.AddCustomAttributes(base.Activity, "FolderPath", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.In));
|
||||||
|
builder.AddCustomAttributes(base.Activity, "OwnerSids", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.In));
|
||||||
|
builder.AddCustomAttributes(base.Activity, "ReaderSids", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.In));
|
||||||
|
builder.AddCustomAttributes(base.Activity, "WriterSids", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.In));
|
||||||
|
builder.AddCustomAttributes(base.Activity, "EnsureTraverse", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.In));
|
||||||
|
builder.AddCustomAttributes(base.Activity, "Success", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.Out));
|
||||||
|
builder.AddCustomAttributes(base.Activity, "ResultToken", new EditorAttribute(typeof(VariablesEditor), typeof(DialogPropertyValueEditor)), new VariablesEditorArgumentsAttribute(allowCreateNew: true, ArgumentDirection.Out));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user