19 Commits

Author SHA1 Message Date
Meik
44954bdd39 Ensure traverse ACLs for NTFS shares 2026-06-16 12:30:45 +02:00
Meik
e0a1fc9595 Document NTFS naming convention placeholders 2026-06-16 11:38:07 +02:00
Meik
8d43d47b82 Bump shared assembly version to 3.3.2 2026-06-16 11:34:29 +02:00
Meik
8394d1e67e Fix README table pipe escaping 2026-05-19 20:39:08 +02:00
Meik
0c16e8a5b1 Document additional provider parameters 2026-05-19 20:35:51 +02:00
Meik
a9b4cfe10b Pin Active Directory provider to domain controller 2026-05-19 20:01:52 +02:00
Meik
723eae1018 Log NTFS ACL naming matches 2026-05-19 19:20:25 +02:00
Meik
0cad46ddef Pin NTFS AD operations to domain controller 2026-05-19 19:12:33 +02:00
Meik
ea0517d1dc Select Consulting4IT signing certificate 2026-05-12 13:03:12 +02:00
Meik
b5c5c12523 Bump version to 3.3.1 2026-05-12 12:50:17 +02:00
Meik
8db5a5d2e6 Derive NTFS traverse scope from group strategy 2026-05-09 23:25:15 +02:00
Meik
2a290b5f86 Sanitize AD unsafe NTFS group name characters 2026-05-08 23:19:54 +02:00
Meik
2b460ccc1a Add configurable NTFS group name formatting 2026-05-08 21:45:36 +02:00
Meik
b9edd16cab Add NTFS root path naming placeholders 2026-05-08 21:34:38 +02:00
Meik
123aa5bf2d Document NTFS traverse ensure configuration 2026-05-08 21:27:42 +02:00
Meik
fc8e907011 Align NTFS ensure traverse handling 2026-05-08 21:25:00 +02:00
Meik
898ed7dd8e Add LIAM permission validation script 2026-05-04 13:54:27 +02:00
Meik
fc64573491 Update LIAM setup installer 2026-05-04 13:52:21 +02:00
Meik
d0a788fa10 Add Bruker delta setup package 2026-03-31 09:03:35 +02:00
40 changed files with 15104 additions and 124 deletions

View File

@@ -30,6 +30,8 @@ namespace C4IT.LIAM
public class cLiamProviderAD : cLiamProviderBase public class cLiamProviderAD : cLiamProviderBase
{ {
public static Guid adModuleId = new Guid("e820a625-0653-ee11-b886-00155d300101"); public static Guid adModuleId = new Guid("e820a625-0653-ee11-b886-00155d300101");
private const string AdditionalConfigurationAdDomainControllersKey = "AdDomainControllers";
private const string AdditionalConfigurationActiveDirectoryDomainControllersKey = "ActiveDirectoryDomainControllers";
public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase(); public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
private readonly ADServiceGroupCreator _serviceGroupCreator; private readonly ADServiceGroupCreator _serviceGroupCreator;
@@ -76,6 +78,7 @@ namespace C4IT.LIAM
var LI = new cADLogonInfo() var LI = new cADLogonInfo()
{ {
Domain = Domain, Domain = Domain,
DomainControllers = GetConfiguredDomainControllers(),
User = Credential?.Identification, User = Credential?.Identification,
UserSecret = Credential?.Secret, UserSecret = Credential?.Secret,
TargetGroupPath = this.GroupPath TargetGroupPath = this.GroupPath
@@ -95,6 +98,26 @@ namespace C4IT.LIAM
return false; return false;
} }
private string GetConfiguredDomainControllers()
{
var value = GetAdditionalConfigurationValue(AdditionalConfigurationAdDomainControllersKey);
if (!string.IsNullOrWhiteSpace(value))
return value;
return GetAdditionalConfigurationValue(AdditionalConfigurationActiveDirectoryDomainControllersKey);
}
private string GetAdditionalConfigurationValue(string key)
{
if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
return string.Empty;
if (!AdditionalConfiguration.TryGetValue(key, out var rawValue) || string.IsNullOrWhiteSpace(rawValue))
return string.Empty;
return rawValue.Trim();
}
public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int Depth = -1) public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int Depth = -1)
{ {
var CM = MethodBase.GetCurrentMethod(); var CM = MethodBase.GetCurrentMethod();
@@ -368,4 +391,4 @@ namespace C4IT.LIAM
this.scope = secGroup.Scope.ToString(); this.scope = secGroup.Scope.ToString();
} }
} }
} }

View File

@@ -52,6 +52,7 @@ namespace LiamAD
public class cADLogonInfo public class cADLogonInfo
{ {
public string Domain; public string Domain;
public string DomainControllers;
public string User; public string User;
public string UserSecret; public string UserSecret;
public string TargetGroupPath; public string TargetGroupPath;

View File

@@ -19,7 +19,6 @@ namespace LiamAD
{ {
private readonly cLiamProviderAD _provider; private readonly cLiamProviderAD _provider;
private readonly cActiveDirectoryBase _adBase; private readonly cActiveDirectoryBase _adBase;
private readonly string _ldapRoot;
private readonly string _user; private readonly string _user;
private readonly string _password; private readonly string _password;
public enum ADGroupType public enum ADGroupType
@@ -31,11 +30,25 @@ namespace LiamAD
{ {
_provider = provider ?? throw new ArgumentNullException(nameof(provider)); _provider = provider ?? throw new ArgumentNullException(nameof(provider));
_adBase = provider.activeDirectoryBase; _adBase = provider.activeDirectoryBase;
_ldapRoot = $"LDAP://{provider.Domain}/{provider.GroupPath}";
_user = provider.Credential.Identification; _user = provider.Credential.Identification;
_password = new System.Net.NetworkCredential(_user, provider.Credential.Secret).Password; _password = new System.Net.NetworkCredential(_user, provider.Credential.Secret).Password;
} }
private string GetLdapServer()
{
return string.IsNullOrWhiteSpace(_adBase.EffectiveDomainController) ? _provider.Domain : _adBase.EffectiveDomainController;
}
private string GetLdapRoot()
{
return $"LDAP://{GetLdapServer()}/{_provider.GroupPath}";
}
private string GetLdapDomainRoot()
{
return $"LDAP://{GetLdapServer()}";
}
/// <summary> /// <summary>
/// Erstellt oder findet beide AD-Gruppen (Member & Owner) für einen Service. /// Erstellt oder findet beide AD-Gruppen (Member & Owner) für einen Service.
/// Neu mit: gruppenbereich (Scope) und gruppentyp (für Member-Gruppe). /// Neu mit: gruppenbereich (Scope) und gruppentyp (für Member-Gruppe).
@@ -115,7 +128,7 @@ namespace LiamAD
if (sidList == null) return; if (sidList == null) return;
// Basis für die Suche: komplette Domäne, nicht nur der OU-Pfad // Basis für die Suche: komplette Domäne, nicht nur der OU-Pfad
string domainRoot = $"LDAP://{_provider.Domain}"; string domainRoot = GetLdapDomainRoot();
using (var root = new DirectoryEntry(domainRoot, _user, _password, AuthenticationTypes.Secure)) using (var root = new DirectoryEntry(domainRoot, _user, _password, AuthenticationTypes.Secure))
using (var grpSearch = new DirectorySearcher(root)) using (var grpSearch = new DirectorySearcher(root))
{ {
@@ -185,7 +198,7 @@ namespace LiamAD
private string GetSid(string name) private string GetSid(string name)
{ {
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure)) using (var root = new DirectoryEntry(GetLdapRoot(), _user, _password, AuthenticationTypes.Secure))
using (var ds = new DirectorySearcher(root)) using (var ds = new DirectorySearcher(root))
{ {
ds.Filter = $"(&(objectCategory=group)(sAMAccountName={name}))"; ds.Filter = $"(&(objectCategory=group)(sAMAccountName={name}))";
@@ -219,7 +232,7 @@ namespace LiamAD
{ {
if (!GroupExists(groupName)) if (!GroupExists(groupName))
{ {
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure)) using (var root = new DirectoryEntry(GetLdapRoot(), _user, _password, AuthenticationTypes.Secure))
{ {
var grp = root.Children.Add("CN=" + groupName, "group"); var grp = root.Children.Add("CN=" + groupName, "group");
grp.Properties["sAMAccountName"].Value = groupName; grp.Properties["sAMAccountName"].Value = groupName;
@@ -261,7 +274,7 @@ namespace LiamAD
private string GetDistinguishedName(string name) private string GetDistinguishedName(string name)
{ {
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure)) using (var root = new DirectoryEntry(GetLdapRoot(), _user, _password, AuthenticationTypes.Secure))
using (var ds = new DirectorySearcher(root)) using (var ds = new DirectorySearcher(root))
{ {
ds.Filter = "(&(objectClass=group)(sAMAccountName=" + name + "))"; ds.Filter = "(&(objectClass=group)(sAMAccountName=" + name + "))";
@@ -285,4 +298,4 @@ namespace LiamAD
} }
} }
} }
} }

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.DirectoryServices; using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.DirectoryServices.AccountManagement; using System.DirectoryServices.AccountManagement;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -25,6 +26,7 @@ namespace LiamAD
private cADLogonInfo privLogonInfo = null; private cADLogonInfo privLogonInfo = null;
public PrincipalContext adContext = null; public PrincipalContext adContext = null;
public DirectoryEntry directoryEntry = null; public DirectoryEntry directoryEntry = null;
public string EffectiveDomainController { get; private set; } = null;
public Exception LastException { get; private set; } = null; public Exception LastException { get; private set; } = null;
public string LastErrorMessage { get; private set; } = null; public string LastErrorMessage { get; private set; } = null;
@@ -50,8 +52,12 @@ namespace LiamAD
//TODO: remove dummy delay? //TODO: remove dummy delay?
await Task.Delay(0); await Task.Delay(0);
ResetError(); ResetError();
adContext = new PrincipalContext(ContextType.Domain, LogonInfo.Domain, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password); var adServer = ResolveEffectiveDomainController(LogonInfo);
var ldapPath = $"LDAP://{LogonInfo.Domain}/{LogonInfo.TargetGroupPath}"; adContext = new PrincipalContext(ContextType.Domain, adServer, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password);
EffectiveDomainController = adContext.ConnectedServer ?? adServer;
LogEntry($"AD provider domain controller pinned to '{EffectiveDomainController}' for domain '{LogonInfo.Domain}'.", LogLevels.Debug);
var ldapPath = $"LDAP://{EffectiveDomainController}/{LogonInfo.TargetGroupPath}";
directoryEntry = new DirectoryEntry directoryEntry = new DirectoryEntry
{ {
Path = ldapPath, Path = ldapPath,
@@ -70,6 +76,78 @@ namespace LiamAD
return false; return false;
} }
private string ResolveEffectiveDomainController(cADLogonInfo logonInfo)
{
var configuredDomainControllers = ParseDomainControllers(logonInfo?.DomainControllers);
foreach (var domainController in configuredDomainControllers)
{
if (CanConnectToDomainController(domainController, logonInfo))
return domainController;
LogEntry($"Configured AD provider domain controller '{domainController}' is not reachable. Trying next candidate.", LogLevels.Warning);
}
var pdc = TryGetPdcRoleOwner(logonInfo);
if (!string.IsNullOrWhiteSpace(pdc))
return pdc;
LogEntry($"Could not determine PDC emulator for domain '{logonInfo?.Domain}'. Falling back to domain locator.", LogLevels.Warning);
return logonInfo?.Domain;
}
private static IEnumerable<string> ParseDomainControllers(string domainControllers)
{
if (string.IsNullOrWhiteSpace(domainControllers))
return Enumerable.Empty<string>();
return domainControllers
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(i => i.Trim())
.Where(i => !string.IsNullOrWhiteSpace(i));
}
private bool CanConnectToDomainController(string domainController, cADLogonInfo logonInfo)
{
try
{
using (var context = new PrincipalContext(ContextType.Domain, domainController, logonInfo.User, new NetworkCredential("", logonInfo.UserSecret).Password))
return !string.IsNullOrWhiteSpace(context.ConnectedServer);
}
catch (Exception E)
{
LogException(E, LogLevels.Debug);
return false;
}
}
private string TryGetPdcRoleOwner(cADLogonInfo logonInfo)
{
try
{
var credentials = new DirectoryContext(
DirectoryContextType.Domain,
logonInfo.Domain,
logonInfo.User,
new NetworkCredential("", logonInfo.UserSecret).Password);
using (var domain = Domain.GetDomain(credentials))
return domain?.PdcRoleOwner?.Name;
}
catch (Exception E)
{
LogException(E, LogLevels.Debug);
return null;
}
}
private string GetAdServer()
{
if (!string.IsNullOrWhiteSpace(EffectiveDomainController))
return EffectiveDomainController;
return privLogonInfo?.Domain;
}
private async Task<bool> privRelogon() private async Task<bool> privRelogon()
{ {
if (privLogonInfo == null) if (privLogonInfo == null)
@@ -193,7 +271,7 @@ namespace LiamAD
string managedByDn = k.Properties["managedBy"][0].ToString(); string managedByDn = k.Properties["managedBy"][0].ToString();
// Erstellen eines DirectoryEntry-Objekts für den managedBy-DN // Erstellen eines DirectoryEntry-Objekts für den managedBy-DN
using (DirectoryEntry managedByEntry = new DirectoryEntry($"LDAP://{managedByDn}")) using (DirectoryEntry managedByEntry = new DirectoryEntry($"LDAP://{GetAdServer()}/{managedByDn}", privLogonInfo.User, new NetworkCredential("", privLogonInfo.UserSecret).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing))
{ {
if (managedByEntry.Properties.Contains("objectSid") && managedByEntry.Properties["objectSid"].Count > 0) if (managedByEntry.Properties.Contains("objectSid") && managedByEntry.Properties["objectSid"].Count > 0)
{ {
@@ -513,4 +591,4 @@ namespace LiamAD
return null; return null;
} }
} }
} }

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<NewDataSet>
<BasicSchemaObjectClass>
<ID>72d6dc7b-bc2b-f111-2986-00155d320605</ID>
<Name>C4IT_LIAMDataAreaClassification</Name>
<IsSchemaObject>0</IsSchemaObject>
<Schema-MetaInfo>
<Name>DisplayExpression</Name>
<Value>DisplayString</Value>
</Schema-MetaInfo>
<DisplayName>C4IT - LIAM - Data Area Classification</DisplayName>
<BasicSchemaObjectClass-CI>
<LCID>7</LCID>
<DisplayName>C4IT - LIAM - Data Area Classification</DisplayName>
</BasicSchemaObjectClass-CI>
<ProtectionLevel>4</ProtectionLevel>
<ClassType>4</ClassType>
<DisableHistory>1</DisableHistory>
<BasicSchemaAttributeClass>
<ID>75d6dc7b-bc2b-f111-2986-00155d320605</ID>
<Name>Position</Name>
<IsSchemaObject>0</IsSchemaObject>
<Description>Position for the pickup</Description>
<DisplayName>Position</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<Description>Position for the pickup</Description>
<DisplayName>Position</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>2</ValueType>
<AttributeLength>4</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>73d6dc7b-bc2b-f111-2986-00155d320605</ID>
<Name>DisplayString</Name>
<IsSchemaObject>0</IsSchemaObject>
<Description>Display value for the pickup</Description>
<DisplayName>Display String</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<Description>Display value for the pickup</Description>
<DisplayName>Display String</DisplayName>
</BasicSchemaAttributeClass-CI>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>0</ValueType>
<AttributeLength>50</AttributeLength>
<LanguageDependent>1</LanguageDependent>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>74d6dc7b-bc2b-f111-2986-00155d320605</ID>
<Name>Value</Name>
<IsSchemaObject>0</IsSchemaObject>
<Description>ID value for the pickup</Description>
<DisplayName>Value</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<Description>ID value for the pickup</Description>
<DisplayName>Value</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>0</AllowNull>
<ValueType>2</ValueType>
<AttributeLength>4</AttributeLength>
</BasicSchemaAttributeClass>
</BasicSchemaObjectClass>
</NewDataSet>

View File

@@ -0,0 +1,464 @@
<NewDataSet>
<C4IT_LIAMDataAreaClassification>
<ID>f1402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>NtfsServerRoot</DisplayString>
<Value>100</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>f3402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>Unknown</DisplayString>
<Value>0</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>f5402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>NtfsShare</DisplayString>
<Value>101</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>f7402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>NtfsFolder</DisplayString>
<Value>102</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>f9402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>DfsNamespaceRoot</DisplayString>
<Value>103</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>fb402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>MsTeamsTeam</DisplayString>
<Value>401</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>fd402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>MsTeamsChannel</DisplayString>
<Value>402</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>ff402d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>MsTeamsFolder</DisplayString>
<Value>403</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>01412d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>ActiveDirectoryGroup</DisplayString>
<Value>501</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>03412d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>ExchangeSharedMailbox</DisplayString>
<Value>601</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification>
<ID>05412d06-bd2b-f111-2986-00155d320605</ID>
<DisplayString>ExchangeDistributionGroup</DisplayString>
<Value>602</Value>
<Hidden>0</Hidden>
</C4IT_LIAMDataAreaClassification>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f9402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fa402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Dfs Namespace Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f9402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fa402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Dfs Namespace Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fb402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fc402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Team</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f9402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fa402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Dfs Namespace Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fb402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fc402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Team</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fd402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fe402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Channel</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f9402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fa402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Dfs Namespace Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fb402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fc402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Team</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fd402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fe402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Channel</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>ff402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>00412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f9402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fa402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Dfs Namespace Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fb402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fc402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Team</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fd402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fe402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Channel</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>ff402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>00412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>01412d06-bd2b-f111-2986-00155d320605</Owner>
<ID>02412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>ActiveDirectory Group</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f9402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fa402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Dfs Namespace Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fb402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fc402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Team</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fd402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fe402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Channel</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>ff402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>00412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>01412d06-bd2b-f111-2986-00155d320605</Owner>
<ID>02412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>ActiveDirectory Group</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>03412d06-bd2b-f111-2986-00155d320605</Owner>
<ID>04412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Exchange SharedMailbox</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f1402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f2402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Server Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f3402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f4402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Unknown</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f5402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f6402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Share</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f7402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>f8402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Ntfs Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>f9402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fa402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Dfs Namespace Root</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fb402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fc402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Team</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>fd402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>fe402d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Channel</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>ff402d06-bd2b-f111-2986-00155d320605</Owner>
<ID>00412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>MsTeams Folder</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>01412d06-bd2b-f111-2986-00155d320605</Owner>
<ID>02412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>ActiveDirectory Group</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>03412d06-bd2b-f111-2986-00155d320605</Owner>
<ID>04412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Exchange SharedMailbox</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
<C4IT_LIAMDataAreaClassification-CI>
<Owner>05412d06-bd2b-f111-2986-00155d320605</Owner>
<ID>06412d06-bd2b-f111-2986-00155d320605</ID>
<LCID>7</LCID>
<DisplayString>Exchange DistributionGroup</DisplayString>
</C4IT_LIAMDataAreaClassification-CI>
</NewDataSet>

View File

@@ -0,0 +1,221 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<NewDataSet>
<BasicSchemaObjectClass>
<ID>dc38719c-6317-ea11-4881-000c2980fd94</ID>
<Name>C4IT_DataAreaClassBase</Name>
<IsSchemaObject>0</IsSchemaObject>
<Schema-MetaInfo>
<Name>DisplayExpression</Name>
<Value>DisplayName</Value>
</Schema-MetaInfo>
<DisplayName>Data Area Class Base</DisplayName>
<BasicSchemaObjectClass-CI>
<LCID>7</LCID>
<DisplayName>C4IT - LIAM - Data Area Class Base</DisplayName>
</BasicSchemaObjectClass-CI>
<ProtectionLevel>4</ProtectionLevel>
<ClassType>3</ClassType>
<DisableHistory>1</DisableHistory>
<BasicSchemaAttributeClass>
<ID>a05bf2c5-6317-ea11-4881-000c2980fd94</ID>
<Name>technicalName</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>Technical Name</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>Technischer Name</DisplayName>
</BasicSchemaAttributeClass-CI>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>0</ValueType>
<AttributeLength>500</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>defe7e43-bd2b-f111-2986-00155d320605</ID>
<Name>C4IT_Classification</Name>
<IsSchemaObject>0</IsSchemaObject>
<Description />
<DisplayName>Klassifikation</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<Description />
<DisplayName>Classification</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>2</ValueType>
<AttributeLength>4</AttributeLength>
<PickupClass>72d6dc7b-bc2b-f111-2986-00155d320605</PickupClass>
<PickupAttribute>Value</PickupAttribute>
<DefaultValue>0</DefaultValue>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>667d9968-8149-ea11-4981-000c2980fd94</ID>
<Name>level</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>Level</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>Level</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>2</ValueType>
<AttributeLength>0</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>0abc7ef9-601a-ea11-4881-000c2980fd94</ID>
<Name>ImportDate</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>Import Date</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>Importdatum</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>3</ValueType>
<AttributeLength>0</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>9a5bf2c5-6317-ea11-4881-000c2980fd94</ID>
<Name>displayname</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>Displayname</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>Anzeigename</DisplayName>
</BasicSchemaAttributeClass-CI>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>0</ValueType>
<AttributeLength>500</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>9d5bf2c5-6317-ea11-4881-000c2980fd94</ID>
<Name>description</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>Description</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>Beschreibung</DisplayName>
</BasicSchemaAttributeClass-CI>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>8</ValueType>
<AttributeLength>0</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>e43190f2-de03-f011-9484-00155d320629</ID>
<Name>isHiddenService</Name>
<IsSchemaObject>0</IsSchemaObject>
<Description />
<DisplayName>Is Hidden Service</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<Description />
<DisplayName>Is Hidden Service</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>0</AllowNull>
<ValueType>9</ValueType>
<AttributeLength>1</AttributeLength>
<DefaultValue>0</DefaultValue>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>36bb15cb-6517-ea11-4881-000c2980fd94</ID>
<Name>targetType</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>Target Type</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>Zieltyp</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>2</ValueType>
<AttributeLength>0</AttributeLength>
<PickupClass>709c986b-6517-ea11-4881-000c2980fd94</PickupClass>
<PickupAttribute>Value</PickupAttribute>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>043b6b40-0318-ea11-4881-000c2980fd94</ID>
<Name>uniqueID</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>uniqueID</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>uniqueID</DisplayName>
</BasicSchemaAttributeClass-CI>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>0</ValueType>
<AttributeLength>500</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>a92f0d2d-0c4c-ea11-4981-000c2980fd94</ID>
<Name>ConfigurationID</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>ConfigurationID</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>ConfigurationID</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>1</ValueType>
<AttributeLength>0</AttributeLength>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>a68f3f50-7e6f-ef11-7984-00155d320616</ID>
<Name>C4IT_ManagedManually</Name>
<IsSchemaObject>0</IsSchemaObject>
<Description />
<DisplayName>Managed Manually</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<Description />
<DisplayName>Manuell verwaltet</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>0</AllowNull>
<ValueType>9</ValueType>
<AttributeLength>1</AttributeLength>
<DefaultValue>0</DefaultValue>
</BasicSchemaAttributeClass>
<BasicSchemaAttributeClass>
<ID>0dbc7ef9-601a-ea11-4881-000c2980fd94</ID>
<Name>CreationTime</Name>
<IsSchemaObject>0</IsSchemaObject>
<DisplayName>Creation Time</DisplayName>
<BasicSchemaAttributeClass-CI>
<LCID>7</LCID>
<DisplayName>Erstelldatum</DisplayName>
</BasicSchemaAttributeClass-CI>
<DisableHistory>1</DisableHistory>
<ProtectionLevel>4</ProtectionLevel>
<DataProtectionIgnored>0</DataProtectionIgnored>
<AllowNull>1</AllowNull>
<ValueType>3</ValueType>
<AttributeLength>0</AttributeLength>
</BasicSchemaAttributeClass>
</BasicSchemaObjectClass>
</NewDataSet>

View File

@@ -0,0 +1,66 @@
<NewDataSet>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="C4IT_Liam_ConfigClassBase">
<xs:complexType>
<xs:sequence>
<xs:element name="encryptionKey" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="liamEngine" type="xs:base64Binary" minOccurs="0" />
<xs:element name="ID" msdata:DataType="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:string" />
<xs:element name="Name" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Version" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="100" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="UsedInTypeC4IT_LIAMConfigurationType" msdata:DataType="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="C4IT_LIAMConfigurationType">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" msdata:DataType="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
<xs:unique name="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//C4IT_Liam_ConfigClassBase" />
<xs:field xpath="ID" />
</xs:unique>
<xs:unique name="C4IT_LIAMConfigurationType_Constraint1" msdata:ConstraintName="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//C4IT_LIAMConfigurationType" />
<xs:field xpath="ID" />
</xs:unique>
</xs:element>
</xs:schema>
<C4IT_Liam_ConfigClassBase>
<liamEngine></liamEngine>
<ID>d033bf66-6c23-eb11-5481-000c299eb325</ID>
<Name>LIAM</Name>
<Version>3.0.2.0</Version>
<encryptionKey></encryptionKey>
<UsedInTypeC4IT_LIAMConfigurationType>41205ba4-5f49-ca19-488a-08d885904833</UsedInTypeC4IT_LIAMConfigurationType>
</C4IT_Liam_ConfigClassBase>
<C4IT_LIAMConfigurationType>
<ID>41205ba4-5f49-ca19-488a-08d885904833</ID>
</C4IT_LIAMConfigurationType>
</NewDataSet>

View File

@@ -0,0 +1,402 @@
<NewDataSet>
<PDRDataQueryType>
<ID>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</ID>
</PDRDataQueryType>
<PDRDataQueryClassColumn>
<ID>22b86d74-932c-4303-a9e4-0daec33d3dda</ID>
<Expression>targetType</Expression>
<Name>targetType</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Target Type</Title>
<Sorting>0</Sorting>
<Position>25</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>200</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>432acf59-d701-41a1-971a-0de9679a62f2</ID>
<Expression>Owner</Expression>
<Name>Owner</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Owner</Title>
<Sorting>0</Sorting>
<Position>30</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>125</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>1</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>4bfb3d37-581b-40a8-9f05-1a01a07b1404</ID>
<Expression>T(SPSCommonClassBase).Validity</Expression>
<Name>state</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>State</Title>
<Sorting>0</Sorting>
<Position>90</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>150</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>f8572a61-d1a7-46ef-adab-327f8a0148ce</ID>
<Expression>level</Expression>
<Name>level</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Level</Title>
<Sorting>0</Sorting>
<Position>70</Position>
<Alignment>2</Alignment>
<Visibility>0</Visibility>
<Width>75</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>90671323-c941-4a9d-9e8e-328abedf3e04</ID>
<Expression>Read</Expression>
<Name>Read</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Read</Title>
<Sorting>0</Sorting>
<Position>40</Position>
<Alignment>0</Alignment>
<Width>125</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>1</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>dfae2238-9ac8-4d4f-a21a-40bc6ca5e125</ID>
<Expression>displayname</Expression>
<Name>displayname</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Display Name</Title>
<Sorting>0</Sorting>
<Position>10</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>150</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>4738e80c-da40-4e37-995b-5d7c7ff76305</ID>
<Expression>technicalName</Expression>
<Name>technicalName</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Technical Name</Title>
<Sorting>0</Sorting>
<Position>15</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>150</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>3806abba-25bc-455b-97a4-6974f3c448a5</ID>
<Expression>Write</Expression>
<Name>Write</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Write</Title>
<Sorting>0</Sorting>
<Position>35</Position>
<Alignment>0</Alignment>
<Width>125</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>1</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>4d9e7fd4-e98e-4111-876e-86355db545f5</ID>
<Expression>CreationTime</Expression>
<Name>CreationTime</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Creation Time</Title>
<Sorting>0</Sorting>
<Position>65</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>100</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>1</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>8e3fd586-3a37-4893-997d-97831ebbd629</ID>
<Expression>DataAreaConfiguration</Expression>
<Name>DataAreaConfiguration</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Sorting>0</Sorting>
<Position>100</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>200</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>1</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>2881b59c-8998-4cd0-ba64-9eabb6b0d57e</ID>
<Expression>uniqueID</Expression>
<Name>uniqueID</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Unique Id</Title>
<Sorting>0</Sorting>
<Position>55</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>150</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>1</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>c0ca3a8c-63ba-4364-9250-d1732c2b2e7f</ID>
<Expression>ImportDate</Expression>
<Name>ImportDate</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Import Date</Title>
<Sorting>0</Sorting>
<Position>60</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>100</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>d729f3d2-2f50-479d-8b68-e7f4cc19a65e</ID>
<Expression>parent</Expression>
<Name>parent</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Parent</Title>
<Sorting>0</Sorting>
<Position>50</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>125</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>8898b212-c592-4a59-901a-f0ddfd67dbf3</ID>
<Expression>C4IT_Classification</Expression>
<Name>C4IT_Classification</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>Classification</Title>
<Sorting>0</Sorting>
<Position>27</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>120</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>0</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn>
<ID>372fc4e0-130a-4a41-bc55-f55c423e8436</ID>
<Expression>cast(configurationid,string)</Expression>
<Name>configid</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsSystem>0</IsSystem>
<Title>ConfigID</Title>
<Sorting>0</Sorting>
<Position>80</Position>
<Alignment>0</Alignment>
<Visibility>0</Visibility>
<Width>150</Width>
<ShowFilter>0</ShowFilter>
<DisplayType>1</DisplayType>
<TitleType>0</TitleType>
<DisplayImage>0</DisplayImage>
<SearchDisplayType>0</SearchDisplayType>
<IsKeyword>0</IsKeyword>
</PDRDataQueryClassColumn>
<PDRDataQueryClassColumn-CI>
<ID>3e491456-6617-ea11-4881-000c2980fd94</ID>
<Owner>22b86d74-932c-4303-a9e4-0daec33d3dda</Owner>
<LCID>7</LCID>
<Title>Zieltyp</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>3f491456-6617-ea11-4881-000c2980fd94</ID>
<Owner>432acf59-d701-41a1-971a-0de9679a62f2</Owner>
<LCID>7</LCID>
<Title>Owner</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>0ffdec1c-cd8a-ea11-5b83-000c2981464e</ID>
<Owner>4bfb3d37-581b-40a8-9f05-1a01a07b1404</Owner>
<LCID>7</LCID>
<Title>Status</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>95463f4f-f94b-ea11-4981-000c2980fd94</ID>
<Owner>f8572a61-d1a7-46ef-adab-327f8a0148ce</Owner>
<LCID>7</LCID>
<Title>Ebene</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>41491456-6617-ea11-4881-000c2980fd94</ID>
<Owner>90671323-c941-4a9d-9e8e-328abedf3e04</Owner>
<LCID>7</LCID>
<Title>Read</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>3b491456-6617-ea11-4881-000c2980fd94</ID>
<Owner>dfae2238-9ac8-4d4f-a21a-40bc6ca5e125</Owner>
<LCID>7</LCID>
<Title>Anzeigename</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>3c491456-6617-ea11-4881-000c2980fd94</ID>
<Owner>4738e80c-da40-4e37-995b-5d7c7ff76305</Owner>
<LCID>7</LCID>
<Title>Technischer Name</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>40491456-6617-ea11-4881-000c2980fd94</ID>
<Owner>3806abba-25bc-455b-97a4-6974f3c448a5</Owner>
<LCID>7</LCID>
<Title>Write</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>94463f4f-f94b-ea11-4981-000c2980fd94</ID>
<Owner>4d9e7fd4-e98e-4111-876e-86355db545f5</Owner>
<LCID>7</LCID>
<Title>Erstelldatum</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>0cd8153d-708e-ec11-5a84-00155d300101</ID>
<Owner>8e3fd586-3a37-4893-997d-97831ebbd629</Owner>
<LCID>7</LCID>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>92463f4f-f94b-ea11-4981-000c2980fd94</ID>
<Owner>2881b59c-8998-4cd0-ba64-9eabb6b0d57e</Owner>
<LCID>7</LCID>
<Title>uniqueD</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>93463f4f-f94b-ea11-4981-000c2980fd94</ID>
<Owner>c0ca3a8c-63ba-4364-9250-d1732c2b2e7f</Owner>
<LCID>7</LCID>
<Title>Importdatum</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>91463f4f-f94b-ea11-4981-000c2980fd94</ID>
<Owner>d729f3d2-2f50-479d-8b68-e7f4cc19a65e</Owner>
<LCID>7</LCID>
<Title>Übergeordnet</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>120e3cb6-bd2b-f111-2986-00155d320605</ID>
<Owner>8898b212-c592-4a59-901a-f0ddfd67dbf3</Owner>
<LCID>7</LCID>
<Title>Klassifikation</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassColumn-CI>
<ID>8da15e45-0d4c-ea11-4981-000c2980fd94</ID>
<Owner>372fc4e0-130a-4a41-bc55-f55c423e8436</Owner>
<LCID>7</LCID>
<Title>ConfigID</Title>
</PDRDataQueryClassColumn-CI>
<PDRDataQueryClassBase>
<ID>2e491456-6617-ea11-4881-000c2980fd94</ID>
<SchemaClassName>C4IT_DataAreaClassBase</SchemaClassName>
<Name>C4IT LIAM - Data Area</Name>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
<IsDefault>1</IsDefault>
<ImplementationType>d</ImplementationType>
<AllowCache>0</AllowCache>
<CultureSensitive>0</CultureSensitive>
<CacheInterval>60</CacheInterval>
<Tag>2df140c9-a676-4377-ae87-e004bd8db9e7</Tag>
<Priority>0</Priority>
</PDRDataQueryClassBase>
<PDRDataQueryClassBase-CI>
<ID>3a491456-6617-ea11-4881-000c2980fd94</ID>
<Owner>2e491456-6617-ea11-4881-000c2980fd94</Owner>
<LCID>7</LCID>
</PDRDataQueryClassBase-CI>
<PDRAudienceClass>
<ID>cdaf5ef9-e1a4-4afe-819b-708430ec9721</ID>
<Unrestricted>1</Unrestricted>
<Configured>1</Configured>
<ConsiderRelated>0</ConsiderRelated>
<StrictAdminControl>1</StrictAdminControl>
<UsedInTypePDRDataQueryType>2b3d0515-1e4a-c42d-ea1d-08d7798a331c</UsedInTypePDRDataQueryType>
</PDRAudienceClass>
</NewDataSet>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,254 @@
<NewDataSet>
<GDIEImportType>
<ID>da44b12e-d036-a669-debe-76da2b0ce41d</ID>
</GDIEImportType>
<GDIEImportClassAction>
<ID>b1061b84-8449-ea11-4981-000c2980fd94</ID>
<DestClass>f7816b37-6417-ea11-4881-000c2980fd94</DestClass>
<Insert>1</Insert>
<IsType>1</IsType>
<Update>1</Update>
<Delete>0</Delete>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
</GDIEImportClassAction>
<GDIEImportClassBase>
<ID>c9061b84-8449-ea11-4981-000c2980fd94</ID>
<Created>2020-02-07T08:33:29.93Z</Created>
<LastAccessed>2026-03-29T22:22:42.173Z</LastAccessed>
<Name>C4IT - LIAM - DataArea (Import_Update)</Name>
<DestTypeID>f7816b37-6417-ea11-4881-000c2980fd94</DestTypeID>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<IsComplexTable>0</IsComplexTable>
<LastResult>1</LastResult>
<IsSimpleMode>0</IsSimpleMode>
<TimeZoneInfoId>UTC</TimeZoneInfoId>
</GDIEImportClassBase>
<GDIEImportClassBase-CI>
<ID>73990b01-b480-ea11-5481-000c299eb325</ID>
<Owner>c9061b84-8449-ea11-4981-000c2980fd94</Owner>
<LCID>7</LCID>
<Name>C4IT - LIAM - DataArea (Import_Update)</Name>
</GDIEImportClassBase-CI>
<SPSCommonClassBase>
<ID>ca061b84-8449-ea11-4981-000c2980fd94</ID>
<UsedInType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInType>
<TypeID>5842d0e4-4625-4e58-a700-23a3dfaa8293</TypeID>
<CostCenter>816361e3-13e6-4b44-96a9-fb44c63a8e36</CostCenter>
<Location>cf060d4d-5c47-462f-b5d1-7df6c865fd91</Location>
<OU>4df735f2-b4bc-4ce8-92ee-60f40fcf5653</OU>
<Security-OU>4df735f2-b4bc-4ce8-92ee-60f40fcf5653</Security-OU>
<Security-Location>cf060d4d-5c47-462f-b5d1-7df6c865fd91</Security-Location>
<Security-CostCenter>816361e3-13e6-4b44-96a9-fb44c63a8e36</Security-CostCenter>
<Category>3</Category>
</SPSCommonClassBase>
<GDIEImportClassSourceObject>
<ID>a5061b84-8449-ea11-4981-000c2980fd94</ID>
<ObjectType>3</ObjectType>
<Alias>res</Alias>
<IsMainObject>1</IsMainObject>
<Expression>/objects/object</Expression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<IsAttribCentric>0</IsAttribCentric>
</GDIEImportClassSourceObject>
<GDIEImportClassMapRule>
<ID>a7061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>DisplayName</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>displayname</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>a8061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>TechnicalName</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>technicalName</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>aa061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>Owner</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<RelatedAttribName>uniqueID</RelatedAttribName>
<DestAttribName>Owner</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>ab061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>Write</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<RelatedAttribName>uniqueID</RelatedAttribName>
<DestAttribName>Write</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>ac061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>Read</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<RelatedAttribName>uniqueID</RelatedAttribName>
<DestAttribName>Read</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>ad061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>CreatedDate</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>CreationTime</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>ae061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>Level</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>level</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>af061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>TargetType</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<RelatedAttribName>Value</RelatedAttribName>
<DestAttribName>targetType</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>b0061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>ImportDate</DestAttribName>
<RuleType>2</RuleType>
<Order>0</Order>
<TransformExpression>GETDATE()</TransformExpression>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>01e4bada-0c4c-ea11-4981-000c2980fd94</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>ConfigurationId</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>ConfigurationID</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>c422900a-7c42-ec11-5b8c-00155d300101</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>UID</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>1</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>uniqueID</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>563a3205-c047-ec11-9984-00155d300101</ID>
<MatchExpression>Production.UsedInTypeC4IT_GCC_DataAreaCollectorType</MatchExpression>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>ConfigurationId</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>1</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<DestAttribName>DataAreaConfiguration</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassMapRule>
<ID>5a2e298b-bd2b-f111-2986-00155d320605</ID>
<SourceObject>a5061b84-8449-ea11-4981-000c2980fd94</SourceObject>
<SourceColumnName>DataAreaTypeId</SourceColumnName>
<IsClassMatch>0</IsClassMatch>
<IsTypeMatch>0</IsTypeMatch>
<DestClass>dc38719c-6317-ea11-4881-000c2980fd94</DestClass>
<IsStaticColumn>0</IsStaticColumn>
<IsMatchExpression>0</IsMatchExpression>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<RelatedAttribName>Value</RelatedAttribName>
<DestAttribName>C4IT_Classification</DestAttribName>
<RuleType>0</RuleType>
<Order>0</Order>
</GDIEImportClassMapRule>
<GDIEImportClassSourceFile>
<ID>a4061b84-8449-ea11-4981-000c2980fd94</ID>
<UrlType>2</UrlType>
<FileUrl>..\Messages\Data\DataAreacollector\LastResult\C4IT_LIAM_DataArea_FS.xml</FileUrl>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
</GDIEImportClassSourceFile>
<GDIEImportClassDatabase>
<ID>c8061b84-8449-ea11-4981-000c2980fd94</ID>
<SourceType>4</SourceType>
<DataCulture>de-DE</DataCulture>
<UsedInTypeGDIEImportType>da44b12e-d036-a669-debe-76da2b0ce41d</UsedInTypeGDIEImportType>
<UseApplicationDb>0</UseApplicationDb>
</GDIEImportClassDatabase>
</NewDataSet>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<C4IT-M42-SchemaInstaller>
<Variables>
<!-- <Variable Name="USE_WF_Worker" Type="SQL" Default="0">select case when ( SELECT count([Version]) FROM [SchemaProductVersion] where [Version] > '10.1') >=1 and (select top 1 c.WorkflowEngineType from SPSGlobalConfigurationClassBase c) = 1 then 1 else 0 end as useWFWorker</Variable> -->
<!-- <Variable Name="WF_STATE" Type="SQL" Default="0">select case when ( SELECT count([Version]) FROM [SchemaProductVersion] where [Version] > '10.1') >=1 and (select top 1 c.WorkflowEngineType from SPSGlobalConfigurationClassBase c) = 1 then 10102 else 10101 end as wfstate</Variable> -->
</Variables>
<UpdateInfo>
<!-- objects, which will not be removed within an update installation -->
<NoRemovement Type="PDRNavigationItemType" ID="8702a16c-cdc6-c810-b02c-08d5cb9d6f63" Ignore="true"/> <!-- C4IT root navigation object in administration -->
<NoRemovement Type="PDRNavigationItemType" ID="125d587d-f51e-cfd7-ca63-08d653beaaef" Ignore="true"/> <!-- C4IT root navigation object in assets -->
<NoRemovement Type="PDRNavigationItemType" ID="a33503d6-160c-c2fb-9656-08d6667e6094" Ignore="true"/> <!-- C4IT root navigation object in service catalog -->
<NoRemovement Type="PDRNavigationItemType" ID="72aa66c3-61cd-ccf4-57e1-08d65f6d24bb" Ignore="true"/> <!-- C4IT root navigation object in service desk -->
<NoRemovement Type="PLSLXamlComponentType" ID="*"/> <!-- all workflows -->
<NoRemovement Type="PLSLWorkflowConfigurationType" ID="*"/> <!-- all workflows Configurations -->
<NoRemovement Type="PLBEActivationType" ID="*"/> <!-- all engine activations -->
<NoRemovement Type="PLSLServiceTypeWebAPI" ID="*"/> <!-- all web services -->
<NoRemovement Type="PLSLWebServiceOperationType" ID="*"/> <!-- all web service operations -->
<NoRemovement Type="SPSArticleTypeService" ID="*"/> <!-- all services -->
<NoRemovement Type="SVCServiceTypeBundle" ID="*"/> <!-- all service bundles -->
<NoRemovement Type="SVCServiceTypeConfigGroup" ID="*"/> <!-- all service groups -->
<NoRemovement Type="SPSArticleCategoryType" ID="*"/> <!-- all article categories -->
<NoRemovement Type="SVCServiceProvisioningTypeCreateWFChange" ID="*"/> <!-- all 'Provisioning - Create WF Change' objects -->
<NoRemovement Type="SPSActivityTemplateClassBase" ID="*"/> <!-- all activity templates -->
<NoRemovement Type="PLSLWorkflowAssemblyLibraryType" ID="*"/> <!-- all activity assemblies -->
</UpdateInfo>
<Removements>
<!-- removement of unnecessary, disturbing content -->
<Remove>Data/SPSContentClassObjectDialogTab/PublishingUser</Remove>
<Remove>NewDataSet/PDRContentWidgetTemplateView/Owner</Remove>
<Remove>NewDataSet/PDRContentWidgetTemplateCustom/Owner</Remove>
<Remove>NewDataSet/PDRContentWidgetTemplateClassViewCustom/Owner</Remove>
<Remove>NewDataSet/PLSLComponentClassBase/CreatedBy</Remove>
<Remove>NewDataSet/PLSLComponentClassBase/LockedBy</Remove>
<Remove>NewDataSet/PLSLXamlComponentClassVersion/CreatedBy</Remove>
<Remove>NewDataSet/PLSLXamlComponentClassVersion/PublishedBy</Remove>
<Remove>NewDataSet/PLSLXamlComponentClassVersion/PublishedDate</Remove>
<Remove>NewDataSet/GDIEImportClassBase/LastResult</Remove>
<Remove>NewDataSet/GDIEImportClassBase/LastAccessed</Remove>
<Remove>NewDataSet/SchemaRelation-PLSLXamlComponentClassVersion2PLSLComponentClassBase</Remove>
<Remove>NewDataSet/CUSTLM42WizardChangeObjectValueClassBase</Remove>
<!-- version dependend corrections -->
<Remove>NewDataSet/PDRContentWidgetClassBase/Tag</Remove>
<Remove>NewDataSet/PDRDataQueryClassBase/Tag</Remove>
<Remove>NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element/xs:complexType/xs:sequence/xs:element[@name='Tag']</Remove>
<Remove>NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element[@name='SPSAssetClassBase']/xs:complexType/xs:sequence/xs:element[@name='UsedInTypeSPSAssetTypeLicenseOEM']</Remove>
<Remove>NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element[@name='SPSAssetClassOrder']/xs:complexType/xs:sequence/xs:element[@name='UsedInTypeSPSAssetTypeLicenseOEM']</Remove>
<Remove>NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element[@name='SPSAssetClassBase']/xs:complexType/xs:sequence/xs:element[@name='UsedInTypeSPSAssetTypeLicenseUpdate']</Remove>
<Remove>NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element[@name='SPSAssetClassOrder']/xs:complexType/xs:sequence/xs:element[@name='UsedInTypeSPSAssetTypeLicenseUpdate']</Remove>
<!-- Attributes only available in v10 -->
<Remove>
<VersionRange From="*.*.*.*" To="9.1.3.2754"/>
NewDataSet/PLSLXamlComponentClassBase/UseWorkflowWorker
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="9.1.3.2754"/>
NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element[@name='PLSLXamlComponentClassBase']/xs:complexType/xs:sequence/xs:element[@name='UseWorkflowWorker']
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="9.1.3.2754"/>
NewDataSet/PLSLXamlComponentClassBase/AllowCloudWorker
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="9.1.3.2754"/>
NewDataSet/PLSLXamlComponentClassBase/MonitoringLevel
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="9.1.3.2754"/>
NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element[@name='PLSLXamlComponentClassBase']/xs:complexType/xs:sequence/xs:element[@name='AllowCloudWorker']
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="9.1.3.2754"/>
NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element[@name='PLSLXamlComponentClassBase']/xs:complexType/xs:sequence/xs:element[@name='MonitoringLevel']
</Remove>
<Remove>
<VersionRange From="9.0.3.0" To="*.*.*.*"/>
NewDataSet/PDRContentWidgetTemplateClassViewCustom/OriginalBase
</Remove>
<Remove>
<VersionRange From="9.0.3.0" To="*.*.*.*"/>
NewDataSet/xs:schema/xs:element/xs:complexType/xs:choice/xs:element/xs:complexType/xs:sequence/xs:element[@name='OriginalBase']
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="12.0.0.*"/>
//PDRAudienceClass[UsedInTypePDRDataQueryType] | //SchemaRelation-PDRAudienceClass2SPSUserClassBase[Users = //PDRAudienceClass[UsedInTypePDRDataQueryType]/ID] | //SchemaRelation-PDRAudienceClass2SPSSecurityClassRole[Roles = //PDRAudienceClass[UsedInTypePDRDataQueryType]/ID] | //SchemaRelation-PDRAudienceClass2SPSOrgUnitClassBase[OUs = //PDRAudienceClass[UsedInTypePDRDataQueryType]/ID] | //SchemaRelation-PDRAudienceClass2SPSLocationClassBase[Locations = //PDRAudienceClass[UsedInTypePDRDataQueryType]/ID] | //SchemaRelation-PDRAudienceClass2PDRAudienceClass[ApplyTo = //PDRAudienceClass[UsedInTypePDRDataQueryType]/ID]
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="12.0.0.*"/>
//PDRDataQueryClassBase/Priority
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="12.0.0.*"/>
//xs:element[@name='PDRDataQueryClassBase']//xs:element[@name='Priority']
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="12.0.0.*"/>
//xs:element[@name='PDRAudienceClass']//xs:element[@name='UsedInTypePDRDataQueryType']
</Remove>
<Remove>
<VersionRange From="*.*.*.*" To="12.0.1.1123"/>
NewDataSet/PDRActionClassBase/ReloadPageOnSuccess
</Remove>
<!-- removement of object ids -->
<Remove>NewDataSet/SPSArticleClassBase/ArticleID</Remove>
<Remove>NewDataSet/SPSOrgUnitClassBase/ObjectId</Remove>
<Remove>NewDataSet/SPSCostCenterClassBase/ObjectId</Remove>
<Remove>NewDataSet/SPSLocationClassBase/ObjectId</Remove>
<Remove>NewDataSet/SPSSupplierClassBase/ObjectId</Remove>
<Remove>NewDataSet/SPSStockKeepingUnitClassBase/ObjectId</Remove>
<Remove>NewDataSet/SPSAssetClassBase/ObjectId</Remove>
<Remove>NewDataSet/SVCServiceProvisioningClassBaseCreateWFChange/ConfigurationID</Remove>
<Remove>NewDataSet/SPSActivityTemplateClassBase/ObjectId</Remove>
<Remove>NewDataSet/SPSArticleCategoryClassBase/ObjectId</Remove>
<!-- set all new installed workflows to state 'new' (10101) -->
<Replace Value="1">/NewDataSet/PLSLXamlComponentClassBase/UseWorkflowWorker</Replace>
<Replace Value="10102">NewDataSet/SPSCommonClassBase[TypeID='b11c6ae9-3d91-e111-489e-6cf049029115']/State</Replace>
<!-- set all new installed compliance rules to state 'inactive' (2036) -->
<Replace Value="2036">NewDataSet/SPSCommonClassBase[TypeID='4083df94-6f60-4cf9-a80c-11522313eebe']/State</Replace>
<Replace Value="%M42ServerInfo_SqlDatabase%">/NewDataSet/GDIEImportClassDatabase/Database</Replace>
<Replace Value="%M42ServerInfo_SqlServer%">/NewDataSet/GDIEImportClassDatabase/Server</Replace>
<!-- set all new installed workflow monitoring level to 0 -->
<Replace Value="0">NewDataSet/PLSLXamlComponentClassBase/MonitoringLevel</Replace>
<!-- set all new installed workflow debug mode to 0 -->
<Replace Value="0">NewDataSet/PLSLXamlComponentClassBase/DebugMode</Replace>
</Removements>
</C4IT-M42-SchemaInstaller>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<C4IT-M42-SchemaInstaller>
<Removements>
<!-- replacements for ownership assignments (replaces all ownerships with the global nodes) -->
<Replace Value="cf060d4d-5c47-462f-b5d1-7df6c865fd91">NewDataSet/SPSCommonClassBase/Location</Replace>
<Replace Value="cf060d4d-5c47-462f-b5d1-7df6c865fd91">NewDataSet/SPSCommonClassBase/Security-Location</Replace>
<Replace Value="4df735f2-b4bc-4ce8-92ee-60f40fcf5653">NewDataSet/SPSCommonClassBase/OU</Replace>
<Replace Value="4df735f2-b4bc-4ce8-92ee-60f40fcf5653">NewDataSet/SPSCommonClassBase/Security-OU</Replace>
<Replace Value="816361e3-13e6-4b44-96a9-fb44c63a8e36">NewDataSet/SPSCommonClassBase/CostCenter</Replace>
<Replace Value="816361e3-13e6-4b44-96a9-fb44c63a8e36">NewDataSet/SPSCommonClassBase/Security-CostCenter</Replace>
<!-- same for root node "Data": replacements for ownership assignments (replaces all ownerships with the global nodes) -->
<Replace Value="cf060d4d-5c47-462f-b5d1-7df6c865fd91">Data/SPSCommonClassBase/Location</Replace>
<Replace Value="cf060d4d-5c47-462f-b5d1-7df6c865fd91">Data/SPSCommonClassBase/Security-Location</Replace>
<Replace Value="4df735f2-b4bc-4ce8-92ee-60f40fcf5653">Data/SPSCommonClassBase/OU</Replace>
<Replace Value="4df735f2-b4bc-4ce8-92ee-60f40fcf5653">Data/SPSCommonClassBase/Security-OU</Replace>
<Replace Value="816361e3-13e6-4b44-96a9-fb44c63a8e36">Data/SPSCommonClassBase/CostCenter</Replace>
<Replace Value="816361e3-13e6-4b44-96a9-fb44c63a8e36">Data/SPSCommonClassBase/Security-CostCenter</Replace>
</Removements>
</C4IT-M42-SchemaInstaller>

Binary file not shown.

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
</startup>
</configuration>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<C4IT-M42-SchemaInstaller Product="C4IT LIAM" Version="3.0.2.0">
<Include>C4IT-M42-SchemaInstaller-Global.xml</Include>
<Include>C4IT-M42-SchemaInstaller-OwnershipCleanup.xml</Include>
<SupportedVersions>
<Version>9.1.2</Version>
<Version>9.1.3</Version>
</SupportedVersions>
<Variables>
<Variable Name="ENCRYPTION_KEY" Type="XMLFILE" Default="">
<FilePath>@M42INSTALLATIONPATH@bin\securityCryptographyConfiguration.config</FilePath>
<XPath>/securityCryptographyConfiguration/databaseEncryption</XPath>
<XAttribute>key</XAttribute>
</Variable>
</Variables>
<UpdateInfo>
<NoRemovement Type="C4IT_SQValidatorPasswordType_Pickup" ID="*"/>
<NoRemovement Type="C4IT_SecurityQuestion_Pickup" ID="*"/>
<NoRemovement Type="C4IT_DurationPeriodPickupType" ID="*"/>
<NoRemovement Type="CTMAppointmentPickupClassType" ID="*"/>
<NoRemovement Type="SPSGenericConnectorType" ID="*"/>
<NoRemovement Type="C4IT_SCPlus_limitedServiceType" ID="66a18f89-ff73-cc79-b9a8-08d73298dcfa"/>
<NoRemovement Type="C4IT_SCPlusConfigurationType" ID="*"/>
<NoRemovement Type="C4IT_LIAMConfigurationType" ID="*"/>
<NoRemovement Type="C4IT_LIAMNamingConventionType" ID="*"/>
<NoRemovement Type="C4IT_GCC_DataAreaCollectorType" ID="*"/>
<NoRemovement Type="SVCServiceProvisioningTypeAssignADGroup" ID="55ffd91e-539f-c08d-4565-08d7385290d1"/>
<NoRemovement Type="PLSCTypeRule" ID="*"/>
<VersionSqlGet>
SELECT version FROM [C4IT_Liam_ConfigClassBase]
WHERE ID='d033bf66-6c23-eb11-5481-000c299eb325'
</VersionSqlGet>
<VersionSqlSet>
UPDATE [C4IT_Liam_ConfigClassBase] SET version=@version
WHERE ID='d033bf66-6c23-eb11-5481-000c299eb325'
</VersionSqlSet>
</UpdateInfo>
<Removements>
<Replace Value="%ENCRYPTION_KEY%">NewDataSet/C4IT_Liam_ConfigClassBase/encryptionKey</Replace>
</Removements>
</C4IT-M42-SchemaInstaller>

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,3 @@
@echo off
cd /D %~dp0
start ..\C4IT-M42-SchemaInstaller.exe develop

View File

@@ -0,0 +1,3 @@
@echo off
cd /D %~dp0
start ..\C4IT-M42-SchemaInstaller.exe enhanced

Binary file not shown.

View File

@@ -0,0 +1,9 @@
[MRU List]
MRU1=C:\Users\dm134\AppData\Local\Temp\tmpDF2B.tmp.exe
MRU2=C:\Users\dm134\AppData\Local\Temp\tmpDE6D.tmp.exe
MRU3=C:\Users\dm134\AppData\Local\Temp\tmpE01D.tmp.exe
MRU4=C:\Users\dm134\AppData\Local\Temp\tmpDE64.tmp.exe
MRU5=C:\Users\dm134\AppData\Local\Temp\tmpDEA2.tmp.exe
MRU6=C:\Users\dm134\AppData\Local\Temp\tmpDDE4.tmp.exe
MRU7=C:\Users\dm134\AppData\Local\Temp\tmpB1CE.tmp.exe
MRU8=C:\Users\dm134\AppData\Local\Temp\tmpB064.tmp.exe

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

View File

@@ -55,6 +55,10 @@ namespace C4IT.LIAM
public static Guid nftsModuleId = new Guid("77e213a1-6517-ea11-4881-000c2980fd94"); public static Guid nftsModuleId = new Guid("77e213a1-6517-ea11-4881-000c2980fd94");
private const string AdditionalConfigurationExcludePathsKey = "NtfsExcludePaths"; private const string AdditionalConfigurationExcludePathsKey = "NtfsExcludePaths";
private const string AdditionalConfigurationIncludePathsKey = "NtfsIncludePaths"; private const string AdditionalConfigurationIncludePathsKey = "NtfsIncludePaths";
private const string AdditionalConfigurationTraverseBoundaryPathKey = "NtfsTraverseBoundaryPath";
private const string AdditionalConfigurationGroupNameSanitizeReplacementKey = "NtfsGroupNameSanitizeReplacement";
private const string AdditionalConfigurationPreserveAdGroupNameCaseKey = "PreserveNtfsAdGroupNameCase";
private const string AdditionalConfigurationAdDomainControllersKey = "NtfsAdDomainControllers";
public readonly cNtfsBase ntfsBase = new cNtfsBase(); public readonly cNtfsBase ntfsBase = new cNtfsBase();
public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase(); public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
private readonly Dictionary<string, HashSet<string>> publishedShareCache = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, HashSet<string>> publishedShareCache = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
@@ -134,6 +138,7 @@ namespace C4IT.LIAM
var LI = new cNtfsLogonInfo() var LI = new cNtfsLogonInfo()
{ {
Domain = Domain, Domain = Domain,
DomainControllers = GetAdditionalConfigurationValue(AdditionalConfigurationAdDomainControllersKey),
User = Credential?.Identification, User = Credential?.Identification,
UserSecret = Credential?.Secret, UserSecret = Credential?.Secret,
TargetNetworkName = RootPath, TargetNetworkName = RootPath,
@@ -944,10 +949,15 @@ namespace C4IT.LIAM
writerSids); writerSids);
engine.WhatIf = whatIf; engine.WhatIf = whatIf;
var allowTraverseGroups = classification.Kind == eNtfsPathKind.Folder && ensureTraverseGroups; var allowTraverseGroups = ensureTraverseGroups
&& IsSupportedPermissionManagedPathKind(
classification,
eNtfsPathKind.Folder,
eNtfsPathKind.ClassicShare,
eNtfsPathKind.DfsLink);
var resultToken = engine.ensureDataAreaPermissions(allowTraverseGroups); var resultToken = engine.ensureDataAreaPermissions(allowTraverseGroups);
if (!allowTraverseGroups && ensureTraverseGroups) if (!allowTraverseGroups && ensureTraverseGroups)
resultToken.warnings.Add($"Traverse groups are currently only ensured for folder paths. Traverse processing was skipped for '{folderPath}'."); resultToken.warnings.Add($"Traverse groups are currently only ensured for folder and share paths. Traverse processing was skipped for '{folderPath}'.");
return Task.FromResult(resultToken); return Task.FromResult(resultToken);
} }
@@ -977,6 +987,7 @@ namespace C4IT.LIAM
{ {
ConfigID = "manual", ConfigID = "manual",
domainName = this.Domain, domainName = this.Domain,
effectiveDomainController = activeDirectoryBase.EffectiveDomainController,
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,
@@ -996,8 +1007,14 @@ namespace C4IT.LIAM
groupDLTag = requiresDomainLocalTag ? GetRequiredCustomTag("Filesystem_GroupDomainLocalTag") : string.Empty, groupDLTag = requiresDomainLocalTag ? GetRequiredCustomTag("Filesystem_GroupDomainLocalTag") : string.Empty,
groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag"), groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag"),
CanManagePermissionsForPath = IsPermissionManagedFolderPath, CanManagePermissionsForPath = IsPermissionManagedFolderPath,
forceStrictAdGroupNames = IsAdditionalConfigurationEnabled("ForceStrictAdGroupNames") CanManageTraversePermissionsForPath = IsTraversePermissionManagedPath,
forceStrictAdGroupNames = IsAdditionalConfigurationEnabled("ForceStrictAdGroupNames"),
groupNameSanitizeReplacement = GetAdditionalConfigurationValueOrDefault(
AdditionalConfigurationGroupNameSanitizeReplacementKey,
Helper.DefaultGroupNameSanitizeReplacement),
preserveAdGroupNameCase = IsAdditionalConfigurationEnabled(AdditionalConfigurationPreserveAdGroupNameCaseKey)
}; };
engine.traverseBoundaryPath = GetAdditionalConfigurationValue(AdditionalConfigurationTraverseBoundaryPathKey);
foreach (var template in BuildSecurityGroupTemplates()) foreach (var template in BuildSecurityGroupTemplates())
engine.templates.Add(template); engine.templates.Add(template);
@@ -1018,6 +1035,28 @@ namespace C4IT.LIAM
|| rawValue.Equals("yes", StringComparison.OrdinalIgnoreCase); || rawValue.Equals("yes", StringComparison.OrdinalIgnoreCase);
} }
private string GetAdditionalConfigurationValue(string key)
{
if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
return string.Empty;
if (!AdditionalConfiguration.TryGetValue(key, out var rawValue) || string.IsNullOrWhiteSpace(rawValue))
return string.Empty;
return rawValue.Trim();
}
private string GetAdditionalConfigurationValueOrDefault(string key, string defaultValue)
{
if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
return defaultValue;
if (!AdditionalConfiguration.TryGetValue(key, out var rawValue))
return defaultValue;
return rawValue == null ? string.Empty : rawValue.Trim();
}
public bool IsPermissionManagedFolderPath(string path) public bool IsPermissionManagedFolderPath(string path)
{ {
return IsPermissionManagedPath(path, eNtfsPathKind.Folder); return IsPermissionManagedPath(path, eNtfsPathKind.Folder);
@@ -1042,6 +1081,23 @@ namespace C4IT.LIAM
return IsPathWhitelisted(classification, false, out matchingConfigurationKey, out matchingRule); return IsPathWhitelisted(classification, false, out matchingConfigurationKey, out matchingRule);
} }
private bool IsTraversePermissionManagedPath(string path)
{
if (string.IsNullOrWhiteSpace(GetAdditionalConfigurationValue(AdditionalConfigurationTraverseBoundaryPathKey)))
return IsPermissionManagedFolderPath(path);
var classification = ClassifyPath(path);
if (classification == null || classification.Kind == eNtfsPathKind.ServerRoot || classification.Kind == eNtfsPathKind.Unknown)
return false;
string matchingConfigurationKey;
string matchingRule;
if (IsPathBlacklisted(classification, out matchingConfigurationKey, out matchingRule))
return false;
return Directory.Exists(path);
}
private static bool IsSupportedPermissionManagedPathKind(cNtfsPathClassification classification, params eNtfsPathKind[] supportedKinds) private static bool IsSupportedPermissionManagedPathKind(cNtfsPathClassification classification, params eNtfsPathKind[] supportedKinds)
{ {
if (classification == null || supportedKinds == null || supportedKinds.Length == 0) if (classification == null || supportedKinds == null || supportedKinds.Length == 0)
@@ -1053,11 +1109,24 @@ namespace C4IT.LIAM
private IEnumerable<IAM_SecurityGroupTemplate> BuildSecurityGroupTemplates() private IEnumerable<IAM_SecurityGroupTemplate> BuildSecurityGroupTemplates()
{ {
var templates = new List<IAM_SecurityGroupTemplate>(); var templates = new List<IAM_SecurityGroupTemplate>();
foreach (var namingConvention in NamingConventions ?? Enumerable.Empty<cLiamNamingConvention>()) var namingConventions = (NamingConventions ?? Enumerable.Empty<cLiamNamingConvention>()).ToList();
var hasStrategyMatchingTraverseConvention = namingConventions.Any(i =>
TryMapSecurityGroupType(i.AccessRole, out var securityGroupType)
&& securityGroupType == SecurityGroupType.Traverse
&& IsStrategyMatchingTraverseScope(i.Scope));
foreach (var namingConvention in namingConventions)
{ {
if (!TryMapSecurityGroupType(namingConvention.AccessRole, out var securityGroupType)) if (!TryMapSecurityGroupType(namingConvention.AccessRole, out var securityGroupType))
continue; continue;
if (securityGroupType == SecurityGroupType.Traverse
&& hasStrategyMatchingTraverseConvention
&& !IsStrategyMatchingTraverseScope(namingConvention.Scope))
{
continue;
}
if (!TryMapGroupScope(namingConvention.Scope, securityGroupType, out var groupScope)) if (!TryMapGroupScope(namingConvention.Scope, securityGroupType, out var groupScope))
continue; continue;
@@ -1097,6 +1166,12 @@ namespace C4IT.LIAM
private bool TryMapGroupScope(eLiamAccessRoleScopes scope, SecurityGroupType type, out GroupScope groupScope) private bool TryMapGroupScope(eLiamAccessRoleScopes scope, SecurityGroupType type, out GroupScope groupScope)
{ {
groupScope = GroupScope.Global; groupScope = GroupScope.Global;
if (type == SecurityGroupType.Traverse)
{
groupScope = GetStrategyTraverseGroupScope();
return true;
}
switch (scope) switch (scope)
{ {
case eLiamAccessRoleScopes.Global: case eLiamAccessRoleScopes.Global:
@@ -1106,17 +1181,30 @@ namespace C4IT.LIAM
groupScope = GroupScope.Local; groupScope = GroupScope.Local;
return true; return true;
case eLiamAccessRoleScopes.Unknown: case eLiamAccessRoleScopes.Unknown:
if (type == SecurityGroupType.Traverse)
{
groupScope = this.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP ? GroupScope.Local : GroupScope.Global;
return true;
}
return false; return false;
default: default:
return false; return false;
} }
} }
private GroupScope GetStrategyTraverseGroupScope()
{
return this.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP
? GroupScope.Local
: GroupScope.Global;
}
private bool IsStrategyMatchingTraverseScope(eLiamAccessRoleScopes scope)
{
if (scope == eLiamAccessRoleScopes.Unknown)
return true;
var strategyScope = GetStrategyTraverseGroupScope();
return strategyScope == GroupScope.Local
? scope == eLiamAccessRoleScopes.DomainLocal
: scope == eLiamAccessRoleScopes.Global;
}
private string GetRequiredCustomTag(string key) private string GetRequiredCustomTag(string key)
{ {
if (CustomTags.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value)) if (CustomTags.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value))
@@ -1267,72 +1355,110 @@ namespace C4IT.LIAM
var traverseNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Traverse); var traverseNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Traverse);
foreach (FileSystemAccessRule rule in ACLs) foreach (FileSystemAccessRule rule in ACLs)
{ {
if (rule.IdentityReference.Value == "S-1-1-0") var aclSid = rule.IdentityReference.Value;
if (aclSid == "S-1-1-0")
continue; continue;
GroupPrincipal grp = GroupPrincipal.FindByIdentity(Provider.activeDirectoryBase.adContext, IdentityType.Sid, rule.IdentityReference.Value); GroupPrincipal grp = GroupPrincipal.FindByIdentity(Provider.activeDirectoryBase.adContext, IdentityType.Sid, aclSid);
if (grp == null) 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; DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' could not be resolved to an AD group. Naming convention matching skipped.");
continue;
}
var samAccountName = grp.SamAccountName ?? string.Empty;
if (string.IsNullOrWhiteSpace(samAccountName))
{
DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{grp.Name}', but no sAMAccountName is available. Naming convention matching skipped.");
continue;
}
if (Regex.IsMatch(samAccountName, ownerNamingConvention.Wildcard, RegexOptions.IgnoreCase))
{
this.OwnerGroupIdentifier = aclSid;
DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Owner naming convention '{ownerNamingConvention.Wildcard}'.");
if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP)
{ {
var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName);
var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter);
var ownerNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Owner && i.Scope == eLiamAccessRoleScopes.Global); var ownerNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Owner && i.Scope == eLiamAccessRoleScopes.Global);
var matchedGlobalGroup = false;
foreach (var memberItem in res) foreach (var memberItem in res ?? new cADCollectionBase())
{ {
var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value);
if (Regex.IsMatch(SecurityGroup.TechnicalName, ownerNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) if (Regex.IsMatch(SecurityGroup.TechnicalName, ownerNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase))
{
this.OwnerGroupIdentifier = SecurityGroup.UID; this.OwnerGroupIdentifier = SecurityGroup.UID;
matchedGlobalGroup = true;
DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Owner ACL group '{samAccountName}' resolved to global group '{SecurityGroup.TechnicalName}' with SID '{SecurityGroup.UID}'.");
}
} }
if (!matchedGlobalGroup)
DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Owner ACL group '{samAccountName}' matched, but no nested global group matched naming convention '{ownerNamingConventionGlobal.Wildcard}'. Keeping ACL SID '{aclSid}'.");
} }
} }
else if (Regex.IsMatch(grp.SamAccountName, writeNamingConvention.Wildcard, RegexOptions.IgnoreCase)) else if (Regex.IsMatch(samAccountName, writeNamingConvention.Wildcard, RegexOptions.IgnoreCase))
{ {
this.WriteGroupIdentifier = rule.IdentityReference.Value; this.WriteGroupIdentifier = aclSid;
DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Write naming convention '{writeNamingConvention.Wildcard}'.");
if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP)
{ {
var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName);
var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter);
var writeNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Write && i.Scope == eLiamAccessRoleScopes.Global); var writeNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Write && i.Scope == eLiamAccessRoleScopes.Global);
var matchedGlobalGroup = false;
foreach (var memberItem in res) foreach (var memberItem in res ?? new cADCollectionBase())
{ {
var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value);
if (Regex.IsMatch(SecurityGroup.TechnicalName, writeNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) if (Regex.IsMatch(SecurityGroup.TechnicalName, writeNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase))
{
this.WriteGroupIdentifier = SecurityGroup.UID; this.WriteGroupIdentifier = SecurityGroup.UID;
matchedGlobalGroup = true;
DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Write ACL group '{samAccountName}' resolved to global group '{SecurityGroup.TechnicalName}' with SID '{SecurityGroup.UID}'.");
}
} }
if (!matchedGlobalGroup)
DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Write ACL group '{samAccountName}' matched, but no nested global group matched naming convention '{writeNamingConventionGlobal.Wildcard}'. Keeping ACL SID '{aclSid}'.");
} }
} }
else if (Regex.IsMatch(grp.SamAccountName, readNamingConvention.Wildcard, RegexOptions.IgnoreCase)) else if (Regex.IsMatch(samAccountName, readNamingConvention.Wildcard, RegexOptions.IgnoreCase))
{ {
this.ReadGroupIdentifier = rule.IdentityReference.Value; this.ReadGroupIdentifier = aclSid;
DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Read naming convention '{readNamingConvention.Wildcard}'.");
if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP)
{ {
var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName);
var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter);
var readNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Read && i.Scope == eLiamAccessRoleScopes.Global); var readNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Read && i.Scope == eLiamAccessRoleScopes.Global);
var matchedGlobalGroup = false;
foreach (var memberItem in res) foreach (var memberItem in res ?? new cADCollectionBase())
{ {
var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value);
if (Regex.IsMatch(SecurityGroup.TechnicalName, readNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) if (Regex.IsMatch(SecurityGroup.TechnicalName, readNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase))
{
this.ReadGroupIdentifier = SecurityGroup.UID; this.ReadGroupIdentifier = SecurityGroup.UID;
matchedGlobalGroup = true;
DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Read ACL group '{samAccountName}' resolved to global group '{SecurityGroup.TechnicalName}' with SID '{SecurityGroup.UID}'.");
}
} }
if (!matchedGlobalGroup)
DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Read ACL group '{samAccountName}' matched, but no nested global group matched naming convention '{readNamingConventionGlobal.Wildcard}'. Keeping ACL SID '{aclSid}'.");
} }
} }
else if (Regex.IsMatch(grp.SamAccountName, traverseNamingConvention.Wildcard, RegexOptions.IgnoreCase)) else if (Regex.IsMatch(samAccountName, traverseNamingConvention.Wildcard, RegexOptions.IgnoreCase))
{ {
this.TraverseGroupIdentifier = rule.IdentityReference.Value; this.TraverseGroupIdentifier = aclSid;
DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Traverse naming convention '{traverseNamingConvention.Wildcard}'.");
} }
else else
{ {
DefaultLogger.LogEntry(LogLevels.Debug, $"No match for: {grp.Name}"); DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}', but did not match Owner/Write/Read/Traverse naming conventions.");
} }
} }
} }

View File

@@ -27,6 +27,7 @@ namespace C4IT_IAM_SET
public const string constApplicationDataPath = "%ProgramData%\\Consulting4IT GmbH\\LIAM"; public const string constApplicationDataPath = "%ProgramData%\\Consulting4IT GmbH\\LIAM";
public string domainName; public string domainName;
public string effectiveDomainController;
public string username; public string username;
public SecureString password; public SecureString password;
private cNetworkConnection Connection; private cNetworkConnection Connection;
@@ -53,7 +54,11 @@ namespace C4IT_IAM_SET
public ICollection<string> readerUserSids; public ICollection<string> readerUserSids;
public ICollection<string> writerUserSids; public ICollection<string> writerUserSids;
public Func<string, bool> CanManagePermissionsForPath; public Func<string, bool> CanManagePermissionsForPath;
public Func<string, bool> CanManageTraversePermissionsForPath;
public string traverseBoundaryPath;
public bool forceStrictAdGroupNames; public bool forceStrictAdGroupNames;
public string groupNameSanitizeReplacement = Helper.DefaultGroupNameSanitizeReplacement;
public bool preserveAdGroupNameCase;
public bool WhatIf; public bool WhatIf;
public int ReadACLPermission = 0x200A9; public int ReadACLPermission = 0x200A9;
@@ -83,6 +88,11 @@ namespace C4IT_IAM_SET
templates = new List<IAM_SecurityGroupTemplate>(); templates = new List<IAM_SecurityGroupTemplate>();
} }
private string GetAdServer()
{
return string.IsNullOrWhiteSpace(effectiveDomainController) ? domainName : effectiveDomainController;
}
private ResultToken checkRequiredVariables() private ResultToken checkRequiredVariables()
{ {
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString()); ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
@@ -147,6 +157,10 @@ namespace C4IT_IAM_SET
DefaultLogger.LogEntry(LogLevels.Info, $"Establishing connection to {baseFolder}, User: {username}, Password: {Helper.MaskAllButLastAndFirst(new NetworkCredential("", password).Password)}"); DefaultLogger.LogEntry(LogLevels.Info, $"Establishing connection to {baseFolder}, User: {username}, Password: {Helper.MaskAllButLastAndFirst(new NetworkCredential("", password).Password)}");
using (Connection = new cNetworkConnection(baseFolder, username, new NetworkCredential("", password).Password)) using (Connection = new cNetworkConnection(baseFolder, username, new NetworkCredential("", password).Password))
{ {
var traverseBoundaryResult = ValidateTraverseBoundaryForCurrentFolder();
if (traverseBoundaryResult.resultErrorId != 0)
return traverseBoundaryResult;
var folderCheckResult = checkFolder(); var folderCheckResult = checkFolder();
if (folderCheckResult.resultErrorId == 0) if (folderCheckResult.resultErrorId == 0)
{ {
@@ -294,11 +308,46 @@ namespace C4IT_IAM_SET
{ {
username = username, username = username,
domainName = domainName, domainName = domainName,
effectiveDomainController = effectiveDomainController,
password = password, password = password,
ForceStrictAdGroupNames = forceStrictAdGroupNames ForceStrictAdGroupNames = forceStrictAdGroupNames,
PreserveAdGroupNameCase = preserveAdGroupNameCase
}; };
} }
private ResultToken ValidateTraverseBoundaryForCurrentFolder()
{
var resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0;
var boundaryPath = GetNormalizedTraverseBoundaryPath();
if (string.IsNullOrWhiteSpace(boundaryPath))
return resultToken;
var targetParent = new DirectoryInfo(newFolderPath).Parent;
if (targetParent == null)
{
resultToken.resultErrorId = 30009;
resultToken.resultMessage = $"Traverse boundary '{traverseBoundaryPath}' cannot be validated because '{newFolderPath}' has no parent directory.";
return resultToken;
}
if (!Directory.Exists(boundaryPath))
{
resultToken.resultErrorId = 30009;
resultToken.resultMessage = $"Traverse boundary '{traverseBoundaryPath}' does not exist or is not reachable.";
return resultToken;
}
if (!IsSameOrAncestorPath(boundaryPath, targetParent.FullName))
{
resultToken.resultErrorId = 30009;
resultToken.resultMessage = $"Traverse boundary '{traverseBoundaryPath}' is not a parent path of '{newFolderPath}'.";
}
return resultToken;
}
public ResultToken ensureDataAreaPermissions(bool ensureTraverseGroups = false) public ResultToken ensureDataAreaPermissions(bool ensureTraverseGroups = false)
{ {
LogMethodBegin(MethodBase.GetCurrentMethod()); LogMethodBegin(MethodBase.GetCurrentMethod());
@@ -327,6 +376,10 @@ namespace C4IT_IAM_SET
InitializeFolderContext(); InitializeFolderContext();
var traverseBoundaryResult = ValidateTraverseBoundaryForCurrentFolder();
if (traverseBoundaryResult.resultErrorId != 0)
return traverseBoundaryResult;
ensureADGroups(resultToken); ensureADGroups(resultToken);
resultToken = ensureFolderPermissions(resultToken); resultToken = ensureFolderPermissions(resultToken);
@@ -395,7 +448,7 @@ namespace C4IT_IAM_SET
return resultToken; return resultToken;
} }
var domainContext = new PrincipalContext(ContextType.Domain, domainName, username, new NetworkCredential("", password).Password); var domainContext = new PrincipalContext(ContextType.Domain, GetAdServer(), username, new NetworkCredential("", password).Password);
DefaultLogger.LogEntry(LogLevels.Debug, "PrincipalContext erfolgreich erstellt."); DefaultLogger.LogEntry(LogLevels.Debug, "PrincipalContext erfolgreich erstellt.");
// Überprüfen von newDataArea und IAM_Folders // Überprüfen von newDataArea und IAM_Folders
@@ -424,6 +477,10 @@ namespace C4IT_IAM_SET
var lvl = DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar); var lvl = DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
DefaultLogger.LogEntry(LogLevels.Debug, $"Ebene (lvl): {lvl}"); DefaultLogger.LogEntry(LogLevels.Debug, $"Ebene (lvl): {lvl}");
var currentTraverseLevel = lvl;
var defaultTraverseLoopIndex = lvl;
var hasTraverseBoundary = !string.IsNullOrWhiteSpace(GetNormalizedTraverseBoundaryPath());
var processedNearestTraverseParent = false;
// Überprüfen der Templates // Überprüfen der Templates
if (templates == null) if (templates == null)
@@ -472,9 +529,9 @@ namespace C4IT_IAM_SET
return resultToken; return resultToken;
} }
for (int i = lvl; i >= createTraverseGroupLvl; i--) while (parent != null && (hasTraverseBoundary || defaultTraverseLoopIndex >= createTraverseGroupLvl))
{ {
DefaultLogger.LogEntry(LogLevels.Debug, $"Verarbeite Ebene {i}."); DefaultLogger.LogEntry(LogLevels.Debug, $"Verarbeite Ebene {currentTraverseLevel}.");
if (parent == null) if (parent == null)
{ {
@@ -482,14 +539,22 @@ namespace C4IT_IAM_SET
break; break;
} }
if (CanManagePermissionsForPath != null && !CanManagePermissionsForPath(parent.FullName)) var canManageTraversePath = CanManageTraversePermissionsForPath ?? CanManagePermissionsForPath;
if (canManageTraversePath != null && !canManageTraversePath(parent.FullName))
{ {
DefaultLogger.LogEntry(LogLevels.Debug, $"Überspringe Traverse-Verarbeitung für nicht verwaltbaren NTFS-Pfad: {parent.FullName}"); DefaultLogger.LogEntry(LogLevels.Debug, $"Überspringe Traverse-Verarbeitung für nicht verwaltbaren NTFS-Pfad: {parent.FullName}");
if (IsTraverseBoundaryPath(parent.FullName))
break;
parent = parent.Parent; parent = parent.Parent;
if (parent != null) if (parent != null)
{ {
lvl = DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar); currentTraverseLevel = hasTraverseBoundary
DefaultLogger.LogEntry(LogLevels.Debug, $"Neue Ebene (lvl) nach Überspringen: {lvl}"); ? currentTraverseLevel + 1
: DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
if (!hasTraverseBoundary)
defaultTraverseLoopIndex--;
DefaultLogger.LogEntry(LogLevels.Debug, $"Neue Ebene (lvl) nach Überspringen: {currentTraverseLevel}");
} }
else else
{ {
@@ -524,44 +589,59 @@ namespace C4IT_IAM_SET
DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath vor Normalisierung: {relativePathRaw}"); DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath vor Normalisierung: {relativePathRaw}");
var relativePathSegments = relativePathRaw.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); var relativePathSegments = relativePathRaw.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
var sanitizedSegments = relativePathSegments.Select(Helper.SanitizePathSegment).ToArray(); var sanitizedSegments = relativePathSegments.Select(i => Helper.SanitizePathSegment(i, groupNameSanitizeReplacement)).ToArray();
var relativePath = sanitizedSegments.Length > 0 ? string.Join("_", sanitizedSegments) : string.Empty; var relativePath = sanitizedSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(sanitizedSegments, groupNameSanitizeReplacement) : string.Empty;
DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath nach Normalisierung: {relativePath}"); DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath nach Normalisierung: {relativePath}");
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)), groupNameSanitizeReplacement);
var traverseTags = GetTraverseReplacementTags(parent.FullName);
var rootContext = Helper.GetRootPathTemplateContext(baseFolder, groupNameSanitizeReplacement);
var boundedTraverseContext = Helper.GetBoundedAdGroupTemplateContext( var boundedTraverseContext = Helper.GetBoundedAdGroupTemplateContext(
traverseGroupTemplate.NamingTemplate, traverseGroupTemplate.NamingTemplate,
true, true,
relativePath, relativePath,
sanitizedSegments, sanitizedSegments,
folderName, folderName,
null, traverseTags,
Helper.MaxAdGroupNameLength, Helper.MaxAdGroupNameLength,
$"Traverse fuer '{parent.FullName}'"); $"Traverse fuer '{parent.FullName}'",
"AD-Gruppenname",
rootContext,
preserveAdGroupNameCase,
groupNameSanitizeReplacement);
var boundedTraverseDescriptionContext = Helper.GetBoundedAdGroupTemplateContext( var boundedTraverseDescriptionContext = Helper.GetBoundedAdGroupTemplateContext(
traverseGroupTemplate.DescriptionTemplate, traverseGroupTemplate.DescriptionTemplate,
true, true,
relativePath, relativePath,
sanitizedSegments, sanitizedSegments,
folderName, folderName,
null, traverseTags,
Helper.MaxAdGroupDescriptionLength, Helper.MaxAdGroupDescriptionLength,
$"Traverse fuer '{parent.FullName}'", $"Traverse fuer '{parent.FullName}'",
"AD-Gruppenbeschreibung"); "AD-Gruppenbeschreibung",
rootContext,
preserveAdGroupNameCase,
groupNameSanitizeReplacement);
var adjustedTraverseSegments = boundedTraverseContext.SanitizedSegments ?? Array.Empty<string>(); var adjustedTraverseSegments = boundedTraverseContext.SanitizedSegments ?? Array.Empty<string>();
var adjustedTraverseRelativePath = adjustedTraverseSegments.Length > 0 ? string.Join("_", adjustedTraverseSegments) : string.Empty; var adjustedTraverseRelativePath = adjustedTraverseSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(adjustedTraverseSegments, groupNameSanitizeReplacement) : string.Empty;
var adjustedTraverseFolderName = boundedTraverseContext.FolderName; var adjustedTraverseFolderName = boundedTraverseContext.FolderName;
var adjustedTraverseDescriptionSegments = boundedTraverseDescriptionContext.SanitizedSegments ?? Array.Empty<string>(); var adjustedTraverseDescriptionSegments = boundedTraverseDescriptionContext.SanitizedSegments ?? Array.Empty<string>();
var adjustedTraverseDescriptionRelativePath = adjustedTraverseDescriptionSegments.Length > 0 ? string.Join("_", adjustedTraverseDescriptionSegments) : string.Empty; var adjustedTraverseDescriptionRelativePath = adjustedTraverseDescriptionSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(adjustedTraverseDescriptionSegments, groupNameSanitizeReplacement) : string.Empty;
var adjustedTraverseDescriptionFolderName = boundedTraverseDescriptionContext.FolderName; var adjustedTraverseDescriptionFolderName = boundedTraverseDescriptionContext.FolderName;
var traverseNameTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName); var traverseNameTemplate = Helper.ApplyAdGroupNameCasing(
var traverseDescriptionTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, adjustedTraverseDescriptionRelativePath, adjustedTraverseDescriptionSegments, adjustedTraverseDescriptionFolderName); Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName, rootContext, groupNameSanitizeReplacement).ReplaceTags(traverseTags),
preserveAdGroupNameCase);
var traverseDescriptionTemplate = Helper.ApplyAdGroupNameCasing(
Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, adjustedTraverseDescriptionRelativePath, adjustedTraverseDescriptionSegments, adjustedTraverseDescriptionFolderName, rootContext, groupNameSanitizeReplacement).ReplaceTags(traverseTags),
preserveAdGroupNameCase);
string traverseRegex = null; string traverseRegex = null;
try try
{ {
traverseRegex = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.WildcardTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName); traverseRegex = Helper.ApplyAdGroupNameCasing(
Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.WildcardTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName, rootContext, groupNameSanitizeReplacement).ReplaceTags(traverseTags),
preserveAdGroupNameCase);
DefaultLogger.LogEntry(LogLevels.Debug, $"traverseRegex: {traverseRegex}"); DefaultLogger.LogEntry(LogLevels.Debug, $"traverseRegex: {traverseRegex}");
} }
catch (Exception ex) catch (Exception ex)
@@ -570,8 +650,12 @@ namespace C4IT_IAM_SET
continue; continue;
} }
var hasTraverseWildcard = !string.IsNullOrWhiteSpace(traverseRegex);
foreach (FileSystemAccessRule acl in ACLs) foreach (FileSystemAccessRule acl in ACLs)
{ {
if (!hasTraverseWildcard)
break;
var searchString = acl.IdentityReference.Value; var searchString = acl.IdentityReference.Value;
var aclSplit = searchString.Split('\\'); var aclSplit = searchString.Split('\\');
if (aclSplit.Length == 2) if (aclSplit.Length == 2)
@@ -600,7 +684,18 @@ namespace C4IT_IAM_SET
break; break;
} }
if (parentTraverseGroup == null && !string.IsNullOrEmpty(traverseNameTemplate)) if (parentTraverseGroup == null && hasTraverseWildcard && !forceStrictAdGroupNames)
{
parentTraverseGroup = FindTraverseGroupByWildcard(domainContext, traverseRegex);
if (parentTraverseGroup != null)
{
resultToken.reusedGroups.Add(parentTraverseGroup.Name);
resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name);
DefaultLogger.LogEntry(LogLevels.Debug, $"Vorhandene Traverse-Gruppe per Wildcard wiederverwendet: {parentTraverseGroup.Name}");
}
}
if (parentTraverseGroup == null && !string.IsNullOrWhiteSpace(traverseNameTemplate))
{ {
for (var loop = 0; loop < 20; loop++) for (var loop = 0; loop < 20; loop++)
{ {
@@ -616,7 +711,7 @@ namespace C4IT_IAM_SET
} }
} }
if (parentTraverseGroup == null && !traverseGroupTemplate.NamingTemplate.Equals(string.Empty)) if (parentTraverseGroup == null && !string.IsNullOrWhiteSpace(traverseNameTemplate))
{ {
DefaultLogger.LogEntry(LogLevels.Debug, "Erstelle neue TraverseGroup."); DefaultLogger.LogEntry(LogLevels.Debug, "Erstelle neue TraverseGroup.");
if (newSecurityGroups == null) if (newSecurityGroups == null)
@@ -646,7 +741,7 @@ namespace C4IT_IAM_SET
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Erstellen von newTraverseGroup: {ex.Message}"); DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Erstellen von newTraverseGroup: {ex.Message}");
break; break;
} }
} while (newSecurityGroups.GroupAllreadyExisting(newTraverseGroup.Name.ToUpper()) && loop < 20); } while (newSecurityGroups.GroupAllreadyExisting(newTraverseGroup.Name) && loop < 20);
if (newTraverseGroup != null) if (newTraverseGroup != null)
{ {
@@ -750,7 +845,7 @@ namespace C4IT_IAM_SET
if (parentTraverseGroup != null) if (parentTraverseGroup != null)
{ {
if (i == lvl) if (!processedNearestTraverseParent)
{ {
DefaultLogger.LogEntry(LogLevels.Debug, "Verarbeite SecurityGroups bei oberster Ebene."); DefaultLogger.LogEntry(LogLevels.Debug, "Verarbeite SecurityGroups bei oberster Ebene.");
foreach (var currentSecGroup in newSecurityGroups.IAM_SecurityGroups) foreach (var currentSecGroup in newSecurityGroups.IAM_SecurityGroups)
@@ -773,6 +868,7 @@ namespace C4IT_IAM_SET
continue; continue;
} }
traverseGroup = parentTraverseGroup; traverseGroup = parentTraverseGroup;
processedNearestTraverseParent = true;
} }
else else
{ {
@@ -821,12 +917,19 @@ namespace C4IT_IAM_SET
if (parentTraverseGroup != null && !resultToken.ensuredTraverseGroups.Contains(parentTraverseGroup.Name)) if (parentTraverseGroup != null && !resultToken.ensuredTraverseGroups.Contains(parentTraverseGroup.Name))
resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name); resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name);
if (IsTraverseBoundaryPath(parent.FullName))
break;
// 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)
{ {
lvl = DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar); currentTraverseLevel = hasTraverseBoundary
DefaultLogger.LogEntry(LogLevels.Debug, $"Neue Ebene (lvl) nach Aktualisierung: {lvl}"); ? currentTraverseLevel + 1
: DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
if (!hasTraverseBoundary)
defaultTraverseLoopIndex--;
DefaultLogger.LogEntry(LogLevels.Debug, $"Neue Ebene (lvl) nach Aktualisierung: {currentTraverseLevel}");
} }
else else
{ {
@@ -847,6 +950,177 @@ namespace C4IT_IAM_SET
} }
} }
private string GetNormalizedTraverseBoundaryPath()
{
return NormalizeDirectoryPath(traverseBoundaryPath);
}
private bool IsTraverseBoundaryPath(string path)
{
var boundaryPath = GetNormalizedTraverseBoundaryPath();
return !string.IsNullOrWhiteSpace(boundaryPath)
&& PathsEqual(boundaryPath, path);
}
private Dictionary<string, string> GetTraverseReplacementTags(string currentPath)
{
var visibleSegments = GetVisibleTraversePathSegments(currentPath);
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "TRAVERSE_NAME", Helper.SanitizePathSegment(GetLastPathSegment(currentPath), groupNameSanitizeReplacement) },
{ "TRAVERSE_VISIBLEPATH", Helper.JoinSanitizedPathSegments(visibleSegments.Select(i => Helper.SanitizePathSegment(i, groupNameSanitizeReplacement)), groupNameSanitizeReplacement) }
};
}
private IEnumerable<string> GetVisibleTraversePathSegments(string currentPath)
{
var normalizedCurrentPath = NormalizeDirectoryPath(currentPath);
var boundaryPath = GetNormalizedTraverseBoundaryPath();
if (string.IsNullOrWhiteSpace(boundaryPath))
boundaryPath = NormalizeDirectoryPath(baseFolder);
var visibleRoot = GetParentPath(boundaryPath);
if (string.IsNullOrWhiteSpace(visibleRoot))
visibleRoot = boundaryPath;
var currentSegments = SplitPathSegments(normalizedCurrentPath);
var rootSegments = SplitPathSegments(visibleRoot);
if (currentSegments.Length <= rootSegments.Length)
return currentSegments;
var isRootPrefix = rootSegments
.Select((segment, index) => new { segment, index })
.All(i => string.Equals(i.segment, currentSegments[i.index], StringComparison.OrdinalIgnoreCase));
return isRootPrefix
? currentSegments.Skip(rootSegments.Length)
: currentSegments;
}
private GroupPrincipal FindTraverseGroupByWildcard(PrincipalContext domainContext, string wildcardPattern)
{
if (domainContext == null || string.IsNullOrWhiteSpace(wildcardPattern))
return null;
Regex wildcardRegex;
try
{
wildcardRegex = new Regex(wildcardPattern, RegexOptions.IgnoreCase);
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
return null;
}
var basePath = "LDAP://" + GetAdServer();
if (!string.IsNullOrWhiteSpace(groupOUPath))
basePath += "/" + groupOUPath;
DirectoryEntry entry = new DirectoryEntry
{
Path = basePath,
Username = username,
Password = new NetworkCredential("", password).Password,
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
};
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(objectClass=group)"
};
search.PageSize = 100000;
search.PropertiesToLoad.Add("sAMAccountName");
search.PropertiesToLoad.Add("objectSid");
string matchedSid = null;
string matchedName = null;
var matchCount = 0;
foreach (SearchResult result in search.FindAll())
{
if (!result.Properties.Contains("sAMAccountName") || result.Properties["sAMAccountName"].Count == 0)
continue;
var samAccountName = result.Properties["sAMAccountName"][0]?.ToString();
if (string.IsNullOrWhiteSpace(samAccountName) || !wildcardRegex.IsMatch(samAccountName))
continue;
matchCount++;
if (matchCount > 1)
{
DefaultLogger.LogEntry(LogLevels.Warning, $"Multiple AD groups matched traverse wildcard '{wildcardPattern}' in '{basePath}'. Regex-based reuse is skipped.");
search.Dispose();
entry.Dispose();
return null;
}
matchedName = samAccountName;
matchedSid = result.Properties.Contains("objectSid") && result.Properties["objectSid"].Count > 0
? new SecurityIdentifier((byte[])result.Properties["objectSid"][0], 0).Value
: null;
}
search.Dispose();
entry.Dispose();
if (string.IsNullOrWhiteSpace(matchedSid))
return null;
DefaultLogger.LogEntry(LogLevels.Debug, $"Reusing existing traverse AD group '{matchedName}' via wildcard '{wildcardPattern}'.");
return GroupPrincipal.FindByIdentity(domainContext, IdentityType.Sid, matchedSid);
}
private static string NormalizeDirectoryPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
return string.Empty;
var normalized = path.Trim().Replace('/', '\\').TrimEnd('\\');
if (normalized.StartsWith(@"\\", StringComparison.Ordinal))
return @"\\" + string.Join("\\", SplitPathSegments(normalized));
return normalized;
}
private static bool PathsEqual(string left, string right)
{
return string.Equals(NormalizeDirectoryPath(left), NormalizeDirectoryPath(right), StringComparison.OrdinalIgnoreCase);
}
private static bool IsSameOrAncestorPath(string ancestorPath, string path)
{
var ancestor = NormalizeDirectoryPath(ancestorPath);
var current = NormalizeDirectoryPath(path);
return string.Equals(ancestor, current, StringComparison.OrdinalIgnoreCase)
|| current.StartsWith(ancestor + "\\", StringComparison.OrdinalIgnoreCase);
}
private static string GetParentPath(string path)
{
var segments = SplitPathSegments(path);
if (segments.Length <= 1)
return string.Empty;
if (NormalizeDirectoryPath(path).StartsWith(@"\\", StringComparison.Ordinal))
return @"\\" + string.Join("\\", segments.Take(segments.Length - 1));
return string.Join("\\", segments.Take(segments.Length - 1));
}
private static string GetLastPathSegment(string path)
{
var segments = SplitPathSegments(path);
return segments.Length == 0 ? string.Empty : segments[segments.Length - 1];
}
private static string[] SplitPathSegments(string path)
{
return (path ?? string.Empty)
.Trim()
.Replace('/', '\\')
.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
}
private bool TryEnsureGlobalGroupMembershipWithRetry(PrincipalContext domainContext, GroupPrincipal parentTraverseGroup, IAM_SecurityGroup currentSecGroup) private bool TryEnsureGlobalGroupMembershipWithRetry(PrincipalContext domainContext, GroupPrincipal parentTraverseGroup, IAM_SecurityGroup currentSecGroup)
{ {
if (domainContext == null || parentTraverseGroup == null || currentSecGroup == null || string.IsNullOrWhiteSpace(currentSecGroup.UID)) if (domainContext == null || parentTraverseGroup == null || currentSecGroup == null || string.IsNullOrWhiteSpace(currentSecGroup.UID))
@@ -946,6 +1220,7 @@ namespace C4IT_IAM_SET
{ {
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString()); ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0; resultToken.resultErrorId = 0;
newSecurityGroups.effectiveDomainController = effectiveDomainController;
if (Directory.Exists(newDataArea.IAM_Folders[0].technicalName)) if (Directory.Exists(newDataArea.IAM_Folders[0].technicalName))
{ {
resultToken.resultMessage = "New folder " + newDataArea.IAM_Folders[0].technicalName + " already exists"; resultToken.resultMessage = "New folder " + newDataArea.IAM_Folders[0].technicalName + " already exists";
@@ -1065,7 +1340,10 @@ namespace C4IT_IAM_SET
ReadACLPermission, ReadACLPermission,
WriteACLPermission, WriteACLPermission,
OwnerACLPermission, OwnerACLPermission,
0); 0,
0,
groupNameSanitizeReplacement,
preserveAdGroupNameCase);
List<UserPrincipal> owners = getUserPrincipalBySid(ownerUserSids); List<UserPrincipal> owners = getUserPrincipalBySid(ownerUserSids);
List<UserPrincipal> writers = getUserPrincipalBySid(writerUserSids); List<UserPrincipal> writers = getUserPrincipalBySid(writerUserSids);
@@ -1228,7 +1506,10 @@ namespace C4IT_IAM_SET
ReadACLPermission, ReadACLPermission,
WriteACLPermission, WriteACLPermission,
OwnerACLPermission, OwnerACLPermission,
existingADGroupCount); existingADGroupCount,
0,
groupNameSanitizeReplacement,
preserveAdGroupNameCase);
/* /*
if (existingADGroupCount > 0 && !templates.All(t => t.Type == SecurityGroupType.Traverse || Regex.IsMatch(t.NamingTemplate, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})"))) if (existingADGroupCount > 0 && !templates.All(t => t.Type == SecurityGroupType.Traverse || Regex.IsMatch(t.NamingTemplate, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})")))
{ {
@@ -1329,7 +1610,7 @@ namespace C4IT_IAM_SET
try try
{ {
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName, username, new NetworkCredential("", password).Password); PrincipalContext ctx = new PrincipalContext(ContextType.Domain, GetAdServer(), username, new NetworkCredential("", password).Password);
UserPrincipal user; UserPrincipal user;
user = UserPrincipal.FindByIdentity(ctx, IdentityType.Sid, (sid)); user = UserPrincipal.FindByIdentity(ctx, IdentityType.Sid, (sid));
return user; return user;

View File

@@ -14,6 +14,8 @@ namespace C4IT_IAM_Engine
public const int MaxAdGroupNameLength = 64; public const int MaxAdGroupNameLength = 64;
public const int MaxAdGroupDescriptionLength = 1024; public const int MaxAdGroupDescriptionLength = 1024;
public const int MaxAdGroupLoopDigits = 3; public const int MaxAdGroupLoopDigits = 3;
public const string DefaultGroupNameSanitizeReplacement = "_";
private const string AdUnsafeGroupNameCharactersPattern = @"[\x00-\x1F\s\-\/\\\[\]:;\|=,\+\*\?<>\@()'""]";
private const int MinLeadingRelativePathSegmentLength = 3; private const int MinLeadingRelativePathSegmentLength = 3;
private const int MinSingleLeadingRelativePathSegmentLength = 2; private const int MinSingleLeadingRelativePathSegmentLength = 2;
private const int MinLastRelativePathSegmentLength = 12; private const int MinLastRelativePathSegmentLength = 12;
@@ -28,6 +30,15 @@ namespace C4IT_IAM_Engine
public string Strategy { get; set; } = string.Empty; public string Strategy { get; set; } = string.Empty;
} }
public sealed class RootPathTemplateContext
{
public string Server { get; set; } = string.Empty;
public string[] Segments { get; set; } = Array.Empty<string>();
public string Name { get; set; } = string.Empty;
public string Path { get; set; } = string.Empty;
public string PathSegmentSeparator { get; set; } = DefaultGroupNameSanitizeReplacement;
}
public static string ReplaceLoopTag(this string str, int loop) public static string ReplaceLoopTag(this string str, int loop)
{ {
return Regex.Replace(str, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})", loop <= 0 ? "" : "${prefix}" + loop + "${postfix}"); return Regex.Replace(str, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})", loop <= 0 ? "" : "${prefix}" + loop + "${postfix}");
@@ -40,11 +51,22 @@ namespace C4IT_IAM_Engine
current.Replace("{{" + value.Key + "}}", value.Value)); current.Replace("{{" + value.Key + "}}", value.Value));
} }
public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName) public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName)
{
return ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, null, DefaultGroupNameSanitizeReplacement);
}
public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName, RootPathTemplateContext rootContext)
{
return ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, rootContext, DefaultGroupNameSanitizeReplacement);
}
public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName, RootPathTemplateContext rootContext, string pathSegmentSeparator)
{ {
if (templateValue == null) if (templateValue == null)
return string.Empty; return string.Empty;
var result = Regex.Replace(templateValue, @"{{\s*NAME\s*}}", folderName ?? string.Empty, RegexOptions.IgnoreCase); var result = Regex.Replace(templateValue, @"{{\s*NAME\s*}}", folderName ?? string.Empty, RegexOptions.IgnoreCase);
result = ApplyRootPathPlaceholders(result, rootContext);
if (allowRelativePath) if (allowRelativePath)
{ {
@@ -61,12 +83,39 @@ namespace C4IT_IAM_Engine
var segmentCount = Math.Min(sanitizedSegments.Length, segmentIndex + 1); var segmentCount = Math.Min(sanitizedSegments.Length, segmentIndex + 1);
var skip = sanitizedSegments.Length - segmentCount; var skip = sanitizedSegments.Length - segmentCount;
return string.Join("_", sanitizedSegments.Skip(skip)); return JoinSanitizedPathSegments(sanitizedSegments.Skip(skip), pathSegmentSeparator);
}, RegexOptions.IgnoreCase); }, RegexOptions.IgnoreCase);
} }
return result; return result;
} }
public static RootPathTemplateContext GetRootPathTemplateContext(string rootPath)
{
return GetRootPathTemplateContext(rootPath, DefaultGroupNameSanitizeReplacement);
}
public static RootPathTemplateContext GetRootPathTemplateContext(string rootPath, string groupNameSanitizeReplacement)
{
var segments = SplitPathSegments(rootPath);
if (segments.Length == 0)
return new RootPathTemplateContext();
var isUncPath = (rootPath ?? string.Empty).Trim().Replace('/', '\\').StartsWith(@"\\", StringComparison.Ordinal);
var server = isUncPath ? SanitizePathSegment(segments[0], groupNameSanitizeReplacement) : string.Empty;
var pathSegments = isUncPath ? segments.Skip(1).ToArray() : segments;
var sanitizedPathSegments = pathSegments.Select(i => SanitizePathSegment(i, groupNameSanitizeReplacement)).ToArray();
return new RootPathTemplateContext
{
Server = server,
Segments = sanitizedPathSegments,
Name = sanitizedPathSegments.Length == 0 ? string.Empty : sanitizedPathSegments[sanitizedPathSegments.Length - 1],
Path = sanitizedPathSegments.Length == 0 ? string.Empty : JoinSanitizedPathSegments(sanitizedPathSegments, groupNameSanitizeReplacement),
PathSegmentSeparator = NormalizeGroupNameSanitizeReplacement(groupNameSanitizeReplacement)
};
}
public static BoundedTemplateContext GetBoundedAdGroupTemplateContext( public static BoundedTemplateContext GetBoundedAdGroupTemplateContext(
string templateValue, string templateValue,
bool allowRelativePath, bool allowRelativePath,
@@ -76,13 +125,16 @@ namespace C4IT_IAM_Engine
IDictionary<string, string> replacementTags, IDictionary<string, string> replacementTags,
int maxLength, int maxLength,
string logContext, string logContext,
string valueLabel = "AD-Gruppenname") string valueLabel = "AD-Gruppenname",
RootPathTemplateContext rootContext = null,
bool preserveCase = false,
string pathSegmentSeparator = DefaultGroupNameSanitizeReplacement)
{ {
var effectiveSegments = (sanitizedSegments ?? Array.Empty<string>()).Where(i => i != null).ToArray(); var effectiveSegments = (sanitizedSegments ?? Array.Empty<string>()).Where(i => i != null).ToArray();
var effectiveFolderName = folderName ?? string.Empty; var effectiveFolderName = folderName ?? string.Empty;
var currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath); var currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath, pathSegmentSeparator);
var originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags); var originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags, rootContext, preserveCase, pathSegmentSeparator);
var measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags); var measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags, rootContext, preserveCase, pathSegmentSeparator);
var usesRelativePath = allowRelativePath && Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*RELATIVEPATH", RegexOptions.IgnoreCase); var usesRelativePath = allowRelativePath && Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*RELATIVEPATH", RegexOptions.IgnoreCase);
var usesName = Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*NAME\s*}}", RegexOptions.IgnoreCase); var usesName = Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*NAME\s*}}", RegexOptions.IgnoreCase);
var strategy = string.Empty; var strategy = string.Empty;
@@ -108,18 +160,21 @@ namespace C4IT_IAM_Engine
if (!changed) if (!changed)
break; break;
currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath); currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath, pathSegmentSeparator);
originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags); originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags, rootContext, preserveCase, pathSegmentSeparator);
measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags); measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags, rootContext, preserveCase, pathSegmentSeparator);
} }
var initialValue = MaterializeTemplateValue( var initialValue = MaterializeTemplateValue(
templateValue, templateValue,
allowRelativePath, allowRelativePath,
GetCurrentRelativePath(sanitizedSegments, defaultRelativePath), GetCurrentRelativePath(sanitizedSegments, defaultRelativePath, pathSegmentSeparator),
sanitizedSegments, sanitizedSegments,
folderName, folderName,
replacementTags); replacementTags,
rootContext,
preserveCase,
pathSegmentSeparator);
var result = new BoundedTemplateContext var result = new BoundedTemplateContext
{ {
SanitizedSegments = effectiveSegments, SanitizedSegments = effectiveSegments,
@@ -147,11 +202,50 @@ namespace C4IT_IAM_Engine
return result; return result;
} }
public static string SanitizePathSegment(string segment) public static string SanitizePathSegment(string segment)
{
return SanitizePathSegment(segment, DefaultGroupNameSanitizeReplacement);
}
public static string SanitizePathSegment(string segment, string groupNameSanitizeReplacement)
{ {
if (string.IsNullOrEmpty(segment)) if (string.IsNullOrEmpty(segment))
return string.Empty; return string.Empty;
return Regex.Replace(segment, @"[\s\-]", "_"); var replacement = NormalizeGroupNameSanitizeReplacement(groupNameSanitizeReplacement);
return Regex.Replace(segment, AdUnsafeGroupNameCharactersPattern, match => replacement);
}
public static string NormalizeGroupNameSanitizeReplacement(string replacement)
{
if (replacement == null)
return DefaultGroupNameSanitizeReplacement;
var trimmed = replacement.Trim();
if (trimmed.Equals("<empty>", StringComparison.OrdinalIgnoreCase)
|| trimmed.Equals("empty", StringComparison.OrdinalIgnoreCase)
|| trimmed.Equals("none", StringComparison.OrdinalIgnoreCase)
|| trimmed.Equals("remove", StringComparison.OrdinalIgnoreCase))
{
return string.Empty;
}
return trimmed;
}
public static string JoinSanitizedPathSegments(IEnumerable<string> sanitizedSegments, string groupNameSanitizeReplacement)
{
if (sanitizedSegments == null)
return string.Empty;
return string.Join(NormalizeGroupNameSanitizeReplacement(groupNameSanitizeReplacement), sanitizedSegments);
}
public static string ApplyAdGroupNameCasing(string value, bool preserveCase)
{
if (value == null)
return string.Empty;
return preserveCase ? value : value.ToUpper();
} }
public static void CreatePathWithWriteAccess(string FilePath) public static void CreatePathWithWriteAccess(string FilePath)
{ {
@@ -181,11 +275,15 @@ namespace C4IT_IAM_Engine
string defaultRelativePath, string defaultRelativePath,
string[] sanitizedSegments, string[] sanitizedSegments,
string folderName, string folderName,
IDictionary<string, string> replacementTags) IDictionary<string, string> replacementTags,
RootPathTemplateContext rootContext,
bool preserveCase,
string pathSegmentSeparator)
{ {
return ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName) var materializedValue = ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, rootContext, pathSegmentSeparator)
.ReplaceTags(replacementTags) .ReplaceTags(replacementTags);
.ToUpper();
return ApplyAdGroupNameCasing(materializedValue, preserveCase);
} }
private static string MaterializeTemplateValueForLength( private static string MaterializeTemplateValueForLength(
@@ -194,10 +292,45 @@ namespace C4IT_IAM_Engine
string defaultRelativePath, string defaultRelativePath,
string[] sanitizedSegments, string[] sanitizedSegments,
string folderName, string folderName,
IDictionary<string, string> replacementTags) IDictionary<string, string> replacementTags,
RootPathTemplateContext rootContext,
bool preserveCase,
string pathSegmentSeparator)
{ {
return NormalizeLoopPlaceholderLength( return NormalizeLoopPlaceholderLength(
MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags)); MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags, rootContext, preserveCase, pathSegmentSeparator));
}
private static string ApplyRootPathPlaceholders(string templateValue, RootPathTemplateContext rootContext)
{
if (templateValue == null)
return string.Empty;
var context = rootContext ?? new RootPathTemplateContext();
var result = Regex.Replace(templateValue, @"{{\s*ROOT_SERVER\s*}}", context.Server ?? string.Empty, RegexOptions.IgnoreCase);
result = Regex.Replace(result, @"{{\s*ROOT_NAME\s*}}", context.Name ?? string.Empty, RegexOptions.IgnoreCase);
result = Regex.Replace(result, @"{{\s*ROOT_PATH(?:\s*\(\s*(\d+)\s*\))?\s*}}", match =>
{
var segments = context.Segments ?? Array.Empty<string>();
if (!match.Groups[1].Success)
return context.Path ?? string.Empty;
if (!int.TryParse(match.Groups[1].Value, out var segmentCount) || segmentCount <= 0)
return string.Empty;
var take = Math.Min(segmentCount, segments.Length);
return take == 0 ? string.Empty : JoinSanitizedPathSegments(segments.Skip(segments.Length - take), context.PathSegmentSeparator);
}, RegexOptions.IgnoreCase);
result = Regex.Replace(result, @"{{\s*ROOT_SEGMENT\s*\(\s*(\d+)\s*\)\s*}}", match =>
{
var segments = context.Segments ?? Array.Empty<string>();
if (!int.TryParse(match.Groups[1].Value, out var segmentIndex) || segmentIndex < 0 || segmentIndex >= segments.Length)
return string.Empty;
return segments[segmentIndex] ?? string.Empty;
}, RegexOptions.IgnoreCase);
return result;
} }
private static string NormalizeLoopPlaceholderLength(string templateValue) private static string NormalizeLoopPlaceholderLength(string templateValue)
@@ -217,10 +350,18 @@ namespace C4IT_IAM_Engine
return NormalizeLoopPlaceholderLength(templateValue).Length; return NormalizeLoopPlaceholderLength(templateValue).Length;
} }
private static string GetCurrentRelativePath(string[] sanitizedSegments, string fallbackRelativePath) private static string[] SplitPathSegments(string path)
{
return (path ?? string.Empty)
.Trim()
.Replace('/', '\\')
.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
}
private static string GetCurrentRelativePath(string[] sanitizedSegments, string fallbackRelativePath, string pathSegmentSeparator)
{ {
if (sanitizedSegments != null && sanitizedSegments.Length > 0) if (sanitizedSegments != null && sanitizedSegments.Length > 0)
return string.Join("_", sanitizedSegments); return JoinSanitizedPathSegments(sanitizedSegments, pathSegmentSeparator);
return fallbackRelativePath ?? string.Empty; return fallbackRelativePath ?? string.Empty;
} }

View File

@@ -21,9 +21,11 @@ namespace C4IT_IAM_Engine
public class SecurityGroups public class SecurityGroups
{ {
public string domainName; public string domainName;
public string effectiveDomainController;
public string username; public string username;
public SecureString password; public SecureString password;
public bool ForceStrictAdGroupNames; public bool ForceStrictAdGroupNames;
public bool PreserveAdGroupNameCase;
public List<IAM_SecurityGroup> IAM_SecurityGroups; public List<IAM_SecurityGroup> IAM_SecurityGroups;
public string rootUID; public string rootUID;
@@ -31,6 +33,11 @@ namespace C4IT_IAM_Engine
{ {
IAM_SecurityGroups = new List<IAM_SecurityGroup>(); IAM_SecurityGroups = new List<IAM_SecurityGroup>();
} }
private string GetLdapServer()
{
return string.IsNullOrWhiteSpace(effectiveDomainController) ? domainName : effectiveDomainController;
}
public bool GroupsAllreadyExisting(string ouPath) public bool GroupsAllreadyExisting(string ouPath)
{ {
LogMethodBegin(MethodBase.GetCurrentMethod()); LogMethodBegin(MethodBase.GetCurrentMethod());
@@ -46,14 +53,14 @@ namespace C4IT_IAM_Engine
{ {
DirectoryEntry entry = new DirectoryEntry DirectoryEntry entry = new DirectoryEntry
{ {
Path = "LDAP://" + domainName, Path = "LDAP://" + GetLdapServer(),
Username = username, Username = username,
Password = new NetworkCredential("", password).Password, Password = new NetworkCredential("", password).Password,
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
}; };
DirectorySearcher dSearch = new DirectorySearcher(entry) DirectorySearcher dSearch = new DirectorySearcher(entry)
{ {
Filter = "(&(CN=" + s.Name.ToUpper() + ")(objectClass=group))" Filter = "(&(CN=" + GetConfiguredGroupName(s.Name) + ")(objectClass=group))"
}; };
dSearch.PageSize = 100000; dSearch.PageSize = 100000;
SearchResultCollection sr = dSearch.FindAll(); SearchResultCollection sr = dSearch.FindAll();
@@ -85,14 +92,14 @@ namespace C4IT_IAM_Engine
{ {
DirectoryEntry entry = new DirectoryEntry DirectoryEntry entry = new DirectoryEntry
{ {
Path = "LDAP://" + domainName, Path = "LDAP://" + GetLdapServer(),
Username = username, Username = username,
Password = new NetworkCredential("", password).Password, Password = new NetworkCredential("", password).Password,
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
}; };
DirectorySearcher dSearch = new DirectorySearcher(entry) DirectorySearcher dSearch = new DirectorySearcher(entry)
{ {
Filter = "(&(CN=" + CN.ToUpper() + ")(objectClass=group))" Filter = "(&(CN=" + GetConfiguredGroupName(CN) + ")(objectClass=group))"
}; };
dSearch.PageSize = 100000; dSearch.PageSize = 100000;
SearchResultCollection sr = dSearch.FindAll(); SearchResultCollection sr = dSearch.FindAll();
@@ -129,7 +136,9 @@ namespace C4IT_IAM_Engine
int writeACLPermission, int writeACLPermission,
int ownerACLPermission, int ownerACLPermission,
int loop = 0, int loop = 0,
int existingADGroupCount = 0) int existingADGroupCount = 0,
string groupNameSanitizeReplacement = Helper.DefaultGroupNameSanitizeReplacement,
bool preserveAdGroupNameCase = false)
{ {
LogMethodBegin(MethodBase.GetCurrentMethod()); LogMethodBegin(MethodBase.GetCurrentMethod());
try try
@@ -145,11 +154,12 @@ namespace C4IT_IAM_Engine
var relativePathRaw = DataArea.GetRelativePath(newFolderPath, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); var relativePathRaw = DataArea.GetRelativePath(newFolderPath, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
var relativePathSegments = relativePathRaw.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); var relativePathSegments = relativePathRaw.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
var sanitizedSegments = relativePathSegments.Select(Helper.SanitizePathSegment).ToArray(); var sanitizedSegments = relativePathSegments.Select(i => Helper.SanitizePathSegment(i, groupNameSanitizeReplacement)).ToArray();
var relativePath = sanitizedSegments.Length > 0 ? string.Join("_", sanitizedSegments) : string.Empty; var relativePath = sanitizedSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(sanitizedSegments, groupNameSanitizeReplacement) : string.Empty;
var folderName = sanitizedSegments.Length > 0 var folderName = sanitizedSegments.Length > 0
? sanitizedSegments[sanitizedSegments.Length - 1] ? sanitizedSegments[sanitizedSegments.Length - 1]
: Helper.SanitizePathSegment(Path.GetFileName(newFolderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))); : Helper.SanitizePathSegment(Path.GetFileName(newFolderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), groupNameSanitizeReplacement);
var rootContext = Helper.GetRootPathTemplateContext(baseFolder, groupNameSanitizeReplacement);
foreach (var template in resolvedTemplates) foreach (var template in resolvedTemplates)
{ {
@@ -206,7 +216,11 @@ namespace C4IT_IAM_Engine
folderName, folderName,
replacementTags, replacementTags,
Helper.MaxAdGroupNameLength, Helper.MaxAdGroupNameLength,
$"{template.Type}/{template.Scope} fuer '{newFolderPath}'"); $"{template.Type}/{template.Scope} fuer '{newFolderPath}'",
"AD-Gruppenname",
rootContext,
preserveAdGroupNameCase,
groupNameSanitizeReplacement);
var boundedDescriptionContext = Helper.GetBoundedAdGroupTemplateContext( var boundedDescriptionContext = Helper.GetBoundedAdGroupTemplateContext(
template.DescriptionTemplate, template.DescriptionTemplate,
@@ -217,27 +231,33 @@ namespace C4IT_IAM_Engine
replacementTags, replacementTags,
Helper.MaxAdGroupDescriptionLength, Helper.MaxAdGroupDescriptionLength,
$"{template.Type}/{template.Scope} fuer '{newFolderPath}'", $"{template.Type}/{template.Scope} fuer '{newFolderPath}'",
"AD-Gruppenbeschreibung"); "AD-Gruppenbeschreibung",
rootContext,
preserveAdGroupNameCase,
groupNameSanitizeReplacement);
var adjustedNameSegments = boundedNameContext.SanitizedSegments ?? Array.Empty<string>(); var adjustedNameSegments = boundedNameContext.SanitizedSegments ?? Array.Empty<string>();
var adjustedNameRelativePath = adjustedNameSegments.Length > 0 ? string.Join("_", adjustedNameSegments) : string.Empty; var adjustedNameRelativePath = adjustedNameSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(adjustedNameSegments, groupNameSanitizeReplacement) : string.Empty;
var adjustedNameFolderName = boundedNameContext.FolderName; var adjustedNameFolderName = boundedNameContext.FolderName;
var adjustedDescriptionSegments = boundedDescriptionContext.SanitizedSegments ?? Array.Empty<string>(); var adjustedDescriptionSegments = boundedDescriptionContext.SanitizedSegments ?? Array.Empty<string>();
var adjustedDescriptionRelativePath = adjustedDescriptionSegments.Length > 0 ? string.Join("_", adjustedDescriptionSegments) : string.Empty; var adjustedDescriptionRelativePath = adjustedDescriptionSegments.Length > 0 ? Helper.JoinSanitizedPathSegments(adjustedDescriptionSegments, groupNameSanitizeReplacement) : string.Empty;
var adjustedDescriptionFolderName = boundedDescriptionContext.FolderName; var adjustedDescriptionFolderName = boundedDescriptionContext.FolderName;
template.NamingTemplate = Helper.ApplyTemplatePlaceholders(template.NamingTemplate, template.Type != SecurityGroupType.Traverse, adjustedNameRelativePath, adjustedNameSegments, adjustedNameFolderName) template.NamingTemplate = Helper.ApplyAdGroupNameCasing(
.ReplaceTags(customTags).ReplaceTags(tags) Helper.ApplyTemplatePlaceholders(template.NamingTemplate, template.Type != SecurityGroupType.Traverse, adjustedNameRelativePath, adjustedNameSegments, adjustedNameFolderName, rootContext, groupNameSanitizeReplacement)
.ToUpper(); .ReplaceTags(customTags).ReplaceTags(tags),
preserveAdGroupNameCase);
template.DescriptionTemplate = Helper.ApplyTemplatePlaceholders(template.DescriptionTemplate, template.Type != SecurityGroupType.Traverse, adjustedDescriptionRelativePath, adjustedDescriptionSegments, adjustedDescriptionFolderName) template.DescriptionTemplate = Helper.ApplyAdGroupNameCasing(
.ReplaceTags(customTags).ReplaceTags(tags) Helper.ApplyTemplatePlaceholders(template.DescriptionTemplate, template.Type != SecurityGroupType.Traverse, adjustedDescriptionRelativePath, adjustedDescriptionSegments, adjustedDescriptionFolderName, rootContext, groupNameSanitizeReplacement)
.ToUpper(); .ReplaceTags(customTags).ReplaceTags(tags),
preserveAdGroupNameCase);
template.WildcardTemplate = Helper.ApplyTemplatePlaceholders(template.WildcardTemplate, template.Type != SecurityGroupType.Traverse, adjustedNameRelativePath, adjustedNameSegments, adjustedNameFolderName) template.WildcardTemplate = Helper.ApplyAdGroupNameCasing(
.ReplaceTags(customTags).ReplaceTags(tags) Helper.ApplyTemplatePlaceholders(template.WildcardTemplate, template.Type != SecurityGroupType.Traverse, adjustedNameRelativePath, adjustedNameSegments, adjustedNameFolderName, rootContext, groupNameSanitizeReplacement)
.ToUpper(); .ReplaceTags(customTags).ReplaceTags(tags),
preserveAdGroupNameCase);
} }
@@ -417,7 +437,7 @@ namespace C4IT_IAM_Engine
{ {
DirectoryEntry entry = new DirectoryEntry DirectoryEntry entry = new DirectoryEntry
{ {
Path = "LDAP://" + domainName, Path = "LDAP://" + GetLdapServer(),
Username = username, Username = username,
Password = new NetworkCredential("", password).Password, Password = new NetworkCredential("", password).Password,
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
@@ -425,7 +445,7 @@ namespace C4IT_IAM_Engine
DirectorySearcher search = new DirectorySearcher(entry) DirectorySearcher search = new DirectorySearcher(entry)
{ {
Filter = "(&(objectClass=group)(sAMAccountName=" + groupName.ToUpper() + "))" Filter = "(&(objectClass=group)(sAMAccountName=" + GetConfiguredGroupName(groupName) + "))"
}; };
search.PageSize = 100000; search.PageSize = 100000;
@@ -459,7 +479,7 @@ namespace C4IT_IAM_Engine
return null; return null;
} }
var basePath = "LDAP://" + domainName; var basePath = "LDAP://" + GetLdapServer();
if (!string.IsNullOrWhiteSpace(ouPath)) if (!string.IsNullOrWhiteSpace(ouPath))
basePath += "/" + ouPath; basePath += "/" + ouPath;
@@ -514,7 +534,7 @@ namespace C4IT_IAM_Engine
return null; return null;
DefaultLogger.LogEntry(LogLevels.Debug, $"Reusing existing AD group '{matchedName}' via wildcard '{wildcardPattern}'."); DefaultLogger.LogEntry(LogLevels.Debug, $"Reusing existing AD group '{matchedName}' via wildcard '{wildcardPattern}'.");
return new DirectoryEntry("LDAP://" + domainName + "/" + matchedDistinguishedName, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing); return new DirectoryEntry("LDAP://" + GetLdapServer() + "/" + matchedDistinguishedName, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
} }
private DirectoryEntry FindGroupEntryFromFolderAcl(string folderPath, string wildcardPattern) private DirectoryEntry FindGroupEntryFromFolderAcl(string folderPath, string wildcardPattern)
@@ -541,7 +561,7 @@ namespace C4IT_IAM_Engine
.Cast<FileSystemAccessRule>(); .Cast<FileSystemAccessRule>();
var matchedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var matchedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
using (var domainContext = new PrincipalContext(ContextType.Domain, domainName, username, new NetworkCredential("", password).Password)) using (var domainContext = new PrincipalContext(ContextType.Domain, GetLdapServer(), username, new NetworkCredential("", password).Password))
{ {
foreach (var rule in rules) foreach (var rule in rules)
{ {
@@ -714,13 +734,17 @@ namespace C4IT_IAM_Engine
try try
{ {
secGroup.CreatedNewEntry = false; secGroup.CreatedNewEntry = false;
if (!GroupAllreadyExisting(secGroup.Name.ToUpper())) var groupName = GetConfiguredGroupName(secGroup.Name);
secGroup.Name = groupName;
secGroup.technicalName = "CN=" + groupName + "," + ouPath;
if (!GroupAllreadyExisting(groupName))
{ {
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domainName + "/" + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing); DirectoryEntry entry = new DirectoryEntry("LDAP://" + GetLdapServer() + "/" + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
DefaultLogger.LogEntry(LogLevels.Debug, $"Creating ad entry with CN / sAmAccountName: {secGroup.Name.ToUpper()}"); DefaultLogger.LogEntry(LogLevels.Debug, $"Creating ad entry with CN / sAmAccountName: {groupName}");
DirectoryEntry group = entry.Children.Add("CN=" + secGroup.Name.ToUpper(), "group"); DirectoryEntry group = entry.Children.Add("CN=" + groupName, "group");
group.Properties["sAmAccountName"].Value = secGroup.Name.ToUpper(); group.Properties["sAmAccountName"].Value = groupName;
if (users != null && secGroup.Scope == GroupScope.Global) if (users != null && secGroup.Scope == GroupScope.Global)
{ {
foreach (var user in users) foreach (var user in users)
@@ -745,7 +769,7 @@ namespace C4IT_IAM_Engine
} }
group.CommitChanges(); group.CommitChanges();
DirectoryEntry ent = new DirectoryEntry("LDAP://" + domainName + "/" + "CN =" + secGroup.Name.ToUpper() + "," + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing); DirectoryEntry ent = new DirectoryEntry("LDAP://" + GetLdapServer() + "/" + "CN=" + groupName + "," + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
var objectid = SecurityGroups.getSID(ent); var objectid = SecurityGroups.getSID(ent);
DefaultLogger.LogEntry(LogLevels.Debug, $"Security group created in ad: {secGroup.technicalName}"); DefaultLogger.LogEntry(LogLevels.Debug, $"Security group created in ad: {secGroup.technicalName}");
@@ -774,6 +798,11 @@ namespace C4IT_IAM_Engine
LogMethodEnd(MethodBase.GetCurrentMethod()); LogMethodEnd(MethodBase.GetCurrentMethod());
} }
} }
private string GetConfiguredGroupName(string groupName)
{
return Helper.ApplyAdGroupNameCasing(groupName, PreserveAdGroupNameCase);
}
} }
public enum GroupScopeValues : int public enum GroupScopeValues : int
{ {

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.DirectoryServices; using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.DirectoryServices.AccountManagement; using System.DirectoryServices.AccountManagement;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -24,6 +25,7 @@ namespace LiamNtfs
private cNtfsLogonInfo privLogonInfo = null; private cNtfsLogonInfo privLogonInfo = null;
public PrincipalContext adContext = null; public PrincipalContext adContext = null;
public DirectoryEntry directoryEntry = null; public DirectoryEntry directoryEntry = null;
public string EffectiveDomainController { get; private set; } = null;
public Exception LastException { get; private set; } = null; public Exception LastException { get; private set; } = null;
public string LastErrorMessage { get; private set; } = null; public string LastErrorMessage { get; private set; } = null;
@@ -49,8 +51,12 @@ namespace LiamNtfs
//TODO: remove dummy delay? //TODO: remove dummy delay?
await Task.Delay(0); await Task.Delay(0);
ResetError(); ResetError();
adContext = new PrincipalContext(ContextType.Domain, LogonInfo.Domain, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password); var adServer = ResolveEffectiveDomainController(LogonInfo);
var ldapPath = $"LDAP://{LogonInfo.Domain}/{LogonInfo.TargetGroupPath}"; adContext = new PrincipalContext(ContextType.Domain, adServer, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password);
EffectiveDomainController = adContext.ConnectedServer ?? adServer;
LogEntry($"NTFS AD domain controller pinned to '{EffectiveDomainController}' for domain '{LogonInfo.Domain}'.", LogLevels.Debug);
var ldapPath = $"LDAP://{EffectiveDomainController}/{LogonInfo.TargetGroupPath}";
directoryEntry = new DirectoryEntry directoryEntry = new DirectoryEntry
{ {
Path = ldapPath, Path = ldapPath,
@@ -69,6 +75,70 @@ namespace LiamNtfs
return false; return false;
} }
private string ResolveEffectiveDomainController(cNtfsLogonInfo logonInfo)
{
var configuredDomainControllers = ParseDomainControllers(logonInfo?.DomainControllers);
foreach (var domainController in configuredDomainControllers)
{
if (CanConnectToDomainController(domainController, logonInfo))
return domainController;
LogEntry($"Configured NTFS AD domain controller '{domainController}' is not reachable. Trying next candidate.", LogLevels.Warning);
}
var pdc = TryGetPdcRoleOwner(logonInfo);
if (!string.IsNullOrWhiteSpace(pdc))
return pdc;
LogEntry($"Could not determine PDC emulator for domain '{logonInfo?.Domain}'. Falling back to domain locator.", LogLevels.Warning);
return logonInfo?.Domain;
}
private static IEnumerable<string> ParseDomainControllers(string domainControllers)
{
if (string.IsNullOrWhiteSpace(domainControllers))
return Enumerable.Empty<string>();
return domainControllers
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(i => i.Trim())
.Where(i => !string.IsNullOrWhiteSpace(i));
}
private bool CanConnectToDomainController(string domainController, cNtfsLogonInfo logonInfo)
{
try
{
using (var context = new PrincipalContext(ContextType.Domain, domainController, logonInfo.User, new NetworkCredential("", logonInfo.UserSecret).Password))
return !string.IsNullOrWhiteSpace(context.ConnectedServer);
}
catch (Exception E)
{
LogException(E, LogLevels.Debug);
return false;
}
}
private string TryGetPdcRoleOwner(cNtfsLogonInfo logonInfo)
{
try
{
var credentials = new DirectoryContext(
DirectoryContextType.Domain,
logonInfo.Domain,
logonInfo.User,
new NetworkCredential("", logonInfo.UserSecret).Password);
using (var domain = Domain.GetDomain(credentials))
return domain?.PdcRoleOwner?.Name;
}
catch (Exception E)
{
LogException(E, LogLevels.Debug);
return null;
}
}
private async Task<bool> privRelogon() private async Task<bool> privRelogon()
{ {
if (privLogonInfo == null) if (privLogonInfo == null)

View File

@@ -334,6 +334,7 @@ namespace LiamNtfs
public class cNtfsLogonInfo public class cNtfsLogonInfo
{ {
public string Domain; public string Domain;
public string DomainControllers;
public string User; public string User;
public string UserSecret; public string UserSecret;
public string TargetNetworkName; public string TargetNetworkName;

View File

@@ -447,7 +447,7 @@ namespace LiamWorkflowActivities
null, null,
null, null,
allowSharePathEnsure, allowSharePathEnsure,
false, ntfsArea is cLiamNtfsFolder || ntfsArea is cLiamNtfsShare,
simulateOnly); simulateOnly);
if (ensureResult == null) if (ensureResult == null)
{ {

View File

@@ -4,6 +4,12 @@ setlocal EnableDelayedExpansion
set "ProductName=C4IT Light Identity Access Management" set "ProductName=C4IT Light Identity Access Management"
set "SignTool=..\..\Common Code\Tools\signtool.exe" set "SignTool=..\..\Common Code\Tools\signtool.exe"
set "TimeStamp=http://rfc3161timestamp.globalsign.com/advanced" set "TimeStamp=http://rfc3161timestamp.globalsign.com/advanced"
set "SignCertificateSubject=Consulting4IT GmbH"
set "SignCertificateEmail=info@consulting4it.de"
if defined LIAM_SIGN_CERT_SUBJECT (
set "SignCertificateSubject=%LIAM_SIGN_CERT_SUBJECT%"
)
REM Alle passenden Dateien in einer Variablen sammeln REM Alle passenden Dateien in einer Variablen sammeln
set "FileList=" set "FileList="
@@ -13,6 +19,11 @@ for %%F in (".\bin\Release\Liam*.dll") do (
REM SignTool mit allen gesammelten Dateien aufrufen REM SignTool mit allen gesammelten Dateien aufrufen
echo Signing all matching files at once... echo Signing all matching files at once...
call "%SignTool%" sign /a /tr %TimeStamp% /td SHA256 /fd SHA256 /d "%ProductName%" !FileList! echo Expected signer: %SignCertificateSubject% ^<%SignCertificateEmail%^>
if defined LIAM_SIGN_CERT_THUMBPRINT (
call "%SignTool%" sign /sha1 "%LIAM_SIGN_CERT_THUMBPRINT%" /tr %TimeStamp% /td SHA256 /fd SHA256 /d "%ProductName%" !FileList!
) else (
call "%SignTool%" sign /n "%SignCertificateSubject%" /tr %TimeStamp% /td SHA256 /fd SHA256 /d "%ProductName%" !FileList!
)
pause pause

View File

@@ -4,6 +4,12 @@ setlocal EnableDelayedExpansion
set "ProductName=C4IT Light Identity Access Management" set "ProductName=C4IT Light Identity Access Management"
set "SignTool=..\..\Common Code\Tools\signtool.exe" set "SignTool=..\..\Common Code\Tools\signtool.exe"
set "TimeStamp=http://rfc3161timestamp.globalsign.com/advanced" set "TimeStamp=http://rfc3161timestamp.globalsign.com/advanced"
set "SignCertificateSubject=Consulting4IT GmbH"
set "SignCertificateEmail=info@consulting4it.de"
if defined LIAM_SIGN_CERT_SUBJECT (
set "SignCertificateSubject=%LIAM_SIGN_CERT_SUBJECT%"
)
set "FileList=" set "FileList="
@@ -24,6 +30,11 @@ if not defined FileList (
) )
echo Signing all matching files at once... echo Signing all matching files at once...
call "%SignTool%" sign /a /tr %TimeStamp% /td SHA256 /fd SHA256 /d "%ProductName%" !FileList! echo Expected signer: %SignCertificateSubject% ^<%SignCertificateEmail%^>
if defined LIAM_SIGN_CERT_THUMBPRINT (
call "%SignTool%" sign /sha1 "%LIAM_SIGN_CERT_THUMBPRINT%" /tr %TimeStamp% /td SHA256 /fd SHA256 /d "%ProductName%" !FileList!
) else (
call "%SignTool%" sign /n "%SignCertificateSubject%" /tr %TimeStamp% /td SHA256 /fd SHA256 /d "%ProductName%" !FileList!
)
pause pause

253
README.md Normal file
View File

@@ -0,0 +1,253 @@
# LIAM
## Naming Conventions fuer NTFS-Gruppen
NTFS-Namenskonventionen bestehen aus `NamingTemplate`, `DescriptionTemplate` und `Wildcard`. Die Templates werden beim Erzeugen oder Sicherstellen von AD-Gruppen materialisiert. Der `Wildcard` ist eine regulaere Expression und wird beim Auslesen der ACLs verwendet, um vorhandene Gruppen wieder einer Rolle zuzuordnen.
Die wichtigsten Eingaben fuer die Ersetzung sind:
| Quelle | Beispiel |
| --- | --- |
| `RootPath` | `\\SRVWSM001.imagoverum.com\file_shares\share2` |
| Aktuelle DataArea | `\\SRVWSM001.imagoverum.com\file_shares\share2\test33` |
| `CustomTags` | `Filesystem_GroupGlobalTag=UG`, `Filesystem_GroupDomainLocalTag=FS`, `Filesystem_GroupOwnerTag=_OW` |
| Rolle / Scope | z. B. `Owner` und `DomainLocal` |
### Standard-Platzhalter
| Platzhalter | Wirkung | Beispielwert bei Root `\\server\file_shares\share2` und DataArea `...\share2\test33` |
| --- | --- | --- |
| `{{NAME}}` | Name des aktuellen Ordners oder Shares. | `test33` |
| `{{RELATIVEPATH}}` | Relativer Pfad der DataArea unterhalb des `RootPath`, mit Sanitizing und konfiguriertem Segmenttrenner. | `test33` |
| `{{RELATIVEPATH(0)}}` | Letztes relatives Pfadsegment. | `test33` |
| `{{RELATIVEPATH(1)}}` | Die letzten zwei relativen Pfadsegmente. Bei weniger Segmenten werden alle vorhandenen verwendet. | z. B. `team1_test33` |
| `{{ROOT_SERVER}}` | Server-/Namespace-Teil des UNC-RootPath. | `server` |
| `{{ROOT_NAME}}` | Letztes Segment des RootPath. | `share2` |
| `{{ROOT_PATH}}` | Alle RootPath-Segmente ohne Server, mit Segmenttrenner verbunden. | `file_shares_share2` |
| `{{ROOT_PATH(1)}}` | Letztes RootPath-Segment. | `share2` |
| `{{ROOT_PATH(2)}}` | Letzte zwei RootPath-Segmente. | `file_shares_share2` |
| `{{ROOT_SEGMENT(0)}}` | RootPath-Segment per 0-basierter Position, ohne Server. | `file_shares` |
| `{{ROOT_SEGMENT(1)}}` | Zweites RootPath-Segment. | `share2` |
`{{RELATIVEPATH(n)}}` zaehlt von hinten: `0` ist das letzte Segment, `1` sind die letzten zwei Segmente, `2` die letzten drei Segmente usw. `{{ROOT_PATH(n)}}` verwendet dagegen die letzten `n` Segmente des RootPath.
Wenn die aktuelle DataArea dem `RootPath` selbst entspricht, ist `{{RELATIVEPATH}}` leer. `{{NAME}}` ist dann der Name des RootPath, also z. B. `share2` oder `LEI.01.test`.
### Rollen- und Scope-Platzhalter
Diese Platzhalter werden aus Rolle, Scope und `CustomTags` aufgebaut:
| Platzhalter | Wirkung | Typische Werte |
| --- | --- | --- |
| `{{PREFIX}}` | Gruppenprefix aus `Filesystem_GroupPrefixTag`, fallback auf `ADGroupPrefix`. | `ACL`, `FS` |
| `{{SCOPETAG}}` | Scope-Tag. Bei `Global` aus `Filesystem_GroupGlobalTag`, bei `DomainLocal` aus `Filesystem_GroupDomainLocalTag`. | `UG`, `FS`, `G`, `DL` |
| `{{GROUPTYPEPOSTFIX}}` | Rollen-Postfix. Owner/Write/Read/Traverse verwenden die jeweiligen `Filesystem_Group*Tag` Werte. | `_OW`, `_RW`, `_R`, `_T` |
| `{{_LOOP}}` | Optionaler Eindeutigkeitszaehler. Beim ersten Versuch leer, danach `_1`, `_2`, usw. | leer, `_1`, `_2` |
| `{{LOOP}}` | Variante ohne Trennzeichen. Beim ersten Versuch leer, danach `1`, `2`, usw. | leer, `1`, `2` |
Der Loop-Platzhalter ist flexibel: Alles innerhalb der geschweiften Klammern, das `LOOP` enthaelt, wird als Loop-Tag behandelt. `{{-LOOP}}` wird beim ersten Versuch leer und danach z. B. `-1`.
### CustomTags
Alle Eintraege aus `CustomTags` koennen ebenfalls direkt verwendet werden. Die Schreibweise muss exakt zum Key passen.
Beispiele:
```text
{{ADGroupPrefix}}_{{RELATIVEPATH}}{{GROUPTYPEPOSTFIX}}
{{Filesystem_GroupGlobalTag}}_{{ROOT_NAME}}_{{NAME}}{{GROUPTYPEPOSTFIX}}
{{Filesystem_GroupDomainLocalTag}}_{{ROOT_PATH(1)}}_{{RELATIVEPATH}}{{GROUPTYPEPOSTFIX}}
```
### Beispiele fuer NamingTemplate
Share-basierte Gruppen nur mit Share-Name:
```text
{{SCOPETAG}}_{{NAME}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
```
Bei `RootPath=\\intra.brkr.corp\File_Shares\LEI.01.test`, Scope `DomainLocal`, `Filesystem_GroupDomainLocalTag=FS` und Rolle `Owner` entsteht:
```text
FS_LEI.01.TEST_OW
```
Gruppen fuer Unterordner relativ zum Root:
```text
{{SCOPETAG}}_{{RELATIVEPATH}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
```
Bei `RootPath=\\SRVWSM001.imagoverum.com\file_shares\share2` und DataArea `...\share2\test33` entsteht fuer Owner:
```text
FS_TEST33_OW
```
Root-Share und Unterordner kombinieren:
```text
{{SCOPETAG}}_{{ROOT_NAME}}.{{RELATIVEPATH}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
```
Bei Root `share2` und Ordner `test33` entsteht:
```text
FS_SHARE2.TEST33_OW
```
Nur die letzten zwei relativen Pfadsegmente verwenden:
```text
{{SCOPETAG}}_{{RELATIVEPATH(1)}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
```
Bei `...\share2\team1\test33` entsteht:
```text
FS_TEAM1_TEST33_OW
```
Namespace-/Share-Bestandteile aus dem RootPath verwenden:
```text
{{SCOPETAG}}_{{ROOT_SEGMENT(0)}}_{{ROOT_SEGMENT(1)}}_{{NAME}}{{GROUPTYPEPOSTFIX}}
```
Bei Root `\\SRVWSM001.imagoverum.com\file_shares\share2` und Name `test33` entsteht:
```text
FS_FILE_SHARES_SHARE2_TEST33_OW
```
### Beispiele fuer AGP und AGDLP
Bei AGP liegen die Global-Gruppen direkt auf der ACL. Eine typische Konvention ist:
```text
NamingTemplate: {{SCOPETAG}}_{{NAME}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
Scope: Global
```
Bei AGDLP liegen die DomainLocal-Gruppen auf der ACL, die Global-Gruppen werden darin verschachtelt. Typisch ist:
```text
Global NamingTemplate: {{SCOPETAG}}_{{NAME}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
DomainLocal NamingTemplate: {{SCOPETAG}}_{{NAME}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
```
Mit `Filesystem_GroupGlobalTag=UG` und `Filesystem_GroupDomainLocalTag=FS` entstehen z. B.:
```text
UG_LEI.01.TEST_OW
FS_LEI.01.TEST_OW
```
In diesem Fall gehoert bei AGDLP die `FS_...` DomainLocal-Gruppe auf die NTFS-ACL; `UG_...` wird Mitglied der `FS_...` Gruppe.
### Wildcard-Regeln
`Wildcard` ist eine regulaere Expression. Sie muss nach Platzhalterersetzung zum erzeugten oder vorhandenen `sAMAccountName` passen.
Passendes Beispiel:
```text
^{{SCOPETAG}}_[a-zA-ZoeaeueOeAeUess0-9_\-\.]*{{GROUPTYPEPOSTFIX}}$
```
Mit `SCOPETAG=FS` und `GROUPTYPEPOSTFIX=_OW` matcht das:
```text
FS_LEI.01.TEST_OW
```
Nicht passend ist ein fuehrendes Leerzeichen:
```text
^ {{SCOPETAG}}_[a-zA-ZoeaeueOeAeUess0-9_\-\.]*{{GROUPTYPEPOSTFIX}}$
```
Dieser Regex erwartet nach `^` ein Leerzeichen und matcht deshalb nicht auf `FS_LEI.01.TEST_OW`.
Wenn eine Traverse-Naming-Convention keine Gruppe erzeugen soll, kann das `NamingTemplate` leer bleiben. Fuer Owner/Write/Read sollte der `Wildcard` nicht leer sein. Ein leerer Regex ist keine sinnvolle Matching-Regel, weil er technisch sehr breit matchen kann.
### Sanitizing, Laenge und Grossschreibung
Pfadbasierte Platzhalter wie `{{NAME}}`, `{{RELATIVEPATH}}`, `{{ROOT_PATH}}` und `{{ROOT_SEGMENT(n)}}` werden fuer AD-Gruppennamen bereinigt. Standardmaessig werden ungueltige Zeichen mit `_` ersetzt. Das Ersatzzeichen kann ueber `NtfsGroupNameSanitizeReplacement` geaendert oder entfernt werden.
Generierte AD-Gruppennamen werden standardmaessig in Grossbuchstaben geschrieben. Mit `PreserveNtfsAdGroupNameCase=1` bleibt die Schreibweise erhalten.
AD-Gruppennamen werden auf die maximale Laenge von 64 Zeichen begrenzt. Wenn ein Template mit `{{RELATIVEPATH}}` oder `{{NAME}}` zu lang wird, werden Pfadbestandteile gekuerzt. Fuer Loop-Platzhalter werden bis zu drei Ziffern reserviert.
## AdditionalParameters / AdditionalConfiguration
Provider-spezifische Zusatzparameter werden in Matrix42 am DataArea-Collector ueber das Fragment `C4IT_GCC_DataArea_Collector_AdditionalAttributes` gepflegt. Pro Parameter wird ein Eintrag mit `Name` und `Value` angelegt.
Beispiel:
| Name | Value |
| --- | --- |
| `EnsureNtfsPermissionGroups` | `1` |
| `NtfsAdDomainControllers` | `dc01.contoso.local,dc02.contoso.local` |
Im Diagnose-JSON erscheinen diese Werte unter `AdditionalConfiguration`. Parameternamen werden ohne Beachtung der Gross-/Kleinschreibung gelesen. Boolean-Werte sind in der Regel aktiv, wenn der Wert `true`, `1` oder `yes` ist. Leere oder nicht vorhandene Werte deaktivieren den jeweiligen Schalter, sofern unten nichts anderes beschrieben ist.
### Allgemein
| Parameter | Provider | Werte | Wirkung |
| --- | --- | --- | --- |
| `WhatIf` | Workflow / alle Provider, soweit unterstuetzt | `true`, `1`, `yes` | Aktiviert den Simulationsmodus fuer Workflow-Pfade, die WhatIf unterstuetzen. Aktionen werden dann vorbereitet und protokolliert, aber nicht dauerhaft ausgefuehrt. |
### NTFS
| Parameter | Werte | Wirkung |
| --- | --- | --- |
| `EnsureNtfsPermissionGroups` | `true`, `1`, `yes` | Stellt beim Auslesen von NTFS-Ordnern automatisch fehlende AD-Berechtigungsgruppen und NTFS-ACLs sicher. |
| `EnsureNtfsPermissionGroupsForShares` | `true`, `1`, `yes` | Erweitert das automatische Ensure auf Share-DataAreas. Ohne diesen Parameter wird das automatische Ensure nur fuer Ordner ausgefuehrt. |
| `AllowManualNtfsPermissionEnsureForShares` | `true`, `1`, `yes` | Erlaubt die manuelle Ensure-Aktivitaet auch fuer Share-DataAreas. |
| `NtfsIncludePaths` | Pfadliste, getrennt mit `;`, &#124; oder Zeilenumbruechen | Beschraenkt die NTFS-Verarbeitung auf passende Pfade. Unterstuetzt relative Pfade unterhalb des RootPath, absolute UNC-Pfade und einfache Wildcards mit `*`. Wenn der Parameter leer ist, sind alle Pfade eingeschlossen. |
| `NtfsExcludePaths` | Pfadliste, getrennt mit `;`, &#124; oder Zeilenumbruechen | Schliesst passende Pfade von der NTFS-Verarbeitung aus. Excludes gewinnen gegen Includes. Unterstuetzt relative Pfade, absolute UNC-Pfade und einfache Wildcards mit `*`. |
| `NtfsTraverseBoundaryPath` | Relativer oder absoluter Pfad | Setzt eine Traverse-Grenze fuer die Traverse-Gruppenverarbeitung. Damit koennen Traverse-Gruppen ueber den eigentlichen Einsprung hinaus bis zu einer definierten Ebene sichergestellt werden. |
| `NtfsGroupNameSanitizeReplacement` | Zeichenfolge, z. B. `_`, `.`, leer, `none`, `remove`, `<empty>` | Legt fest, womit ungueltige Zeichen in dynamischen gruppennamenrelevanten Pfadbestandteilen ersetzt werden. Standard ist `_`. Mit leerem Wert oder `none`/`remove`/`<empty>` werden ungueltige Zeichen entfernt und Pfadsegmente ohne Trennzeichen verbunden. |
| `PreserveNtfsAdGroupNameCase` | `true`, `1`, `yes` | Unterbindet das automatische Uppercase fuer generierte NTFS-AD-Gruppennamen. Ohne diesen Parameter werden generierte Gruppennamen wie bisher in Grossbuchstaben erzeugt. |
| `ForceStrictAdGroupNames` | `true`, `1`, `yes` | Erzwingt strikte AD-Gruppennamen. Wildcard-/ACL-basierte Wiederverwendung abweichender bestehender Gruppen wird damit eingeschraenkt; es werden nur exakt passende konfigurierte oder generierte Namen verwendet. |
| `NtfsAdDomainControllers` | Kommagetrennte DC-Liste, z. B. `dc01.contoso.local,dc02.contoso.local` | Pinnt NTFS-AD-Operationen auf einen Domain Controller. Der erste erreichbare DC wird verwendet. Wenn kein Eintrag erreichbar ist oder der Parameter fehlt, wird der PDC Emulator verwendet; danach faellt der Code auf die normale Domain-Locator-Logik zurueck. Der ausgewaehlte DC wird im Debug-Log protokolliert. |
Beispiele:
```text
EnsureNtfsPermissionGroups=1
EnsureNtfsPermissionGroupsForShares=1
NtfsIncludePaths=Finance\*;HR\Reports
NtfsExcludePaths=*\_archive\*
NtfsTraverseBoundaryPath=\\fileserver\file_shares
NtfsGroupNameSanitizeReplacement=.
PreserveNtfsAdGroupNameCase=1
NtfsAdDomainControllers=dc01.contoso.local,dc02.contoso.local
```
### Active Directory
| Parameter | Werte | Wirkung |
| --- | --- | --- |
| `AdDomainControllers` | Kommagetrennte DC-Liste, z. B. `dc01.contoso.local,dc02.contoso.local` | Pinnt Active-Directory-Provider-Operationen auf einen Domain Controller. Der erste erreichbare DC wird verwendet. Wenn kein Eintrag erreichbar ist oder der Parameter fehlt, wird der PDC Emulator verwendet; danach faellt der Code auf die normale Domain-Locator-Logik zurueck. Der ausgewaehlte DC wird im Debug-Log protokolliert. |
| `ActiveDirectoryDomainControllers` | Kommagetrennte DC-Liste | Alias/Fallback fuer `AdDomainControllers`, wenn `AdDomainControllers` nicht gesetzt ist. |
Beispiel:
```text
AdDomainControllers=dc01.contoso.local,dc02.contoso.local
```
### Microsoft Teams
| Parameter | Werte | Wirkung |
| --- | --- | --- |
| `WithoutPrivateChannels` | `true`, `1` | Private Channels werden beim Teams-Provider nicht beruecksichtigt. |
### Hinweise zur Pfad- und Listen-Syntax
`NtfsIncludePaths` und `NtfsExcludePaths` verwenden `;`, &#124; oder Zeilenumbrueche als Trenner. Kommas sind dort Teil des Werts. Die Domain-Controller-Parameter verwenden dagegen eine kommagetrennte Prioritaetsliste.
Pfadfilter koennen relativ zum konfigurierten NTFS-RootPath oder als absolute UNC-Pfade angegeben werden. Einfache Wildcards mit `*` sind moeglich.

View File

@@ -7,5 +7,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCopyright("Copyright © 2026, Consulting4IT GmbH, Germany")] [assembly: AssemblyCopyright("Copyright © 2026, Consulting4IT GmbH, Germany")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyInformationalVersion("3.3.0")] [assembly: AssemblyInformationalVersion("3.3.2")]
[assembly: AssemblyVersion("3.3.0")] [assembly: AssemblyVersion("3.3.2")]

View File

@@ -107,6 +107,194 @@ Damit kann die Filterung nicht einfach durch direktes Laden einer UID umgangen w
Die Path-Policy ist also klassifizierungsunabhaengig, das Berechtigungs-Handling selbst aber bewusst nicht. Die Path-Policy ist also klassifizierungsunabhaengig, das Berechtigungs-Handling selbst aber bewusst nicht.
### 7. Automatisches Permission-Ensure und Traverse
`EnsureNtfsPermissionGroups` wird ebenfalls ueber `AdditionalConfiguration` gesteuert. Die Werte kommen aus `C4IT_GCC_DataArea_Collector_AdditionalAttributes` und werden beim Provider-Aufbau in `AdditionalConfiguration` uebernommen.
Wenn `EnsureNtfsPermissionGroups=1` gesetzt ist, wird beim Laden von NTFS-Folder-DataAreas der Soll-Zustand fuer Berechtigungsgruppen sichergestellt:
- Owner-/Write-/Read-Gruppen werden angelegt oder wiederverwendet
- fehlende NTFS-ACLs werden auf dem Zielordner gesetzt
- Traverse-Gruppen werden analog zur Ordner-Neuanlage verarbeitet
Damit ist der nachtraegliche Ensure-Pfad fachlich mit der bestehenden Ordner-Neuanlage gleichgezogen. Share-Pfade bleiben gesondert ueber `EnsureNtfsPermissionGroupsForShares` behandelt; Traverse wird dabei nicht implizit fuer Shares erweitert.
### 8. Optionale Traverse-Boundary
Die neue Einstellung `NtfsTraverseBoundaryPath` ist optional und wird ebenfalls aus `AdditionalConfiguration` gelesen.
Ohne `NtfsTraverseBoundaryPath` bleibt die bestehende Traverse-Reichweite unveraendert. Die Verarbeitung folgt dann der bisherigen `baseFolder`-/`createTraverseGroupLvl`-Logik der Ordner-Neuanlage.
Mit `NtfsTraverseBoundaryPath` kann die sichtbare Parent-Kette explizit erweitert werden. Die Traverse-Verarbeitung laeuft dann vom Parent des Zielordners bis inklusive dieser Boundary.
Beispiel:
```text
RootPath=\\SRVWSM001.imagoverum.com\file_shares\share2
EnsureNtfsPermissionGroups=1
NtfsTraverseBoundaryPath=\\SRVWSM001.imagoverum.com\file_shares
```
Fuer den Zielordner:
```text
\\SRVWSM001.imagoverum.com\file_shares\share2\test33
```
werden Traverse-Gruppen und Parent-ACLs fuer folgende Pfade sichergestellt:
- `\\SRVWSM001.imagoverum.com\file_shares\share2`
- `\\SRVWSM001.imagoverum.com\file_shares`
Nicht verarbeitet wird der Serverroot `\\SRVWSM001.imagoverum.com`.
Die Boundary muss ein Parent-Pfad des Zielordners sein und erreichbar sein. Ist das nicht der Fall, bricht der Ensure-/Create-Vorgang mit einer klaren Fehlermeldung ab, bevor Gruppen oder ACLs veraendert werden.
### 9. Traverse-Naming-Platzhalter
Fuer Traverse-Naming-Conventions gibt es zusaetzliche optionale Platzhalter:
- `{{TRAVERSE_NAME}}`: Name des aktuell verarbeiteten Traverse-Parents, z.B. `share2`
- `{{TRAVERSE_VISIBLEPATH}}`: sichtbarer Parent-Pfad ab dem Parent der Boundary, segmentweise mit `_`, z.B. `file_shares_share2`
Bestehende Platzhalter wie `{{NAME}}` und `{{RELATIVEPATH}}` bleiben unveraendert und behalten ihre bisherige Bedeutung.
Beispiel fuer eine Traverse-Namenskonvention:
```text
{{ADGroupPrefix}}_{{SCOPETAG}}_{{TRAVERSE_VISIBLEPATH}}{{_LOOP}}{{GROUPTYPEPOSTFIX}}
```
Bei:
```text
ADGroupPrefix=ACL
Filesystem_GroupGlobalTag=G
Filesystem_GroupTraverseTag=_T
```
entstehen daraus z.B.:
```text
ACL_G_FILE_SHARES_T
ACL_G_FILE_SHARES_SHARE2_T
```
Wenn das Traverse-`NamingTemplate` leer ist, ist das kein Fehler. Es wird dann keine neue Traverse-Gruppe angelegt. Bestehende Gruppen werden aber weiterhin ueber ACLs und, sofern gepflegt, ueber `Wildcard` gesucht und konfiguriert. Sind `NamingTemplate` und `Wildcard` leer, ist die Traverse-Verarbeitung fuer diesen Parent ein No-op.
Der Scope von Traverse-Gruppen wird aus der NTFS-Gruppenstrategie abgeleitet:
- `Ntfs_AGP`: Traverse-Gruppen werden als Global-Gruppen erstellt
- `Ntfs_AGDLP`: Traverse-Gruppen werden als DomainLocal-Gruppen erstellt
Wenn mehrere Traverse-Naming-Conventions vorhanden sind, wird die zur Strategie passende Konvention bevorzugt. Ist keine passende Konvention vorhanden, wird die vorhandene Traverse-Konvention weiterverwendet, der Scope aber trotzdem strategy-driven gesetzt.
### 10. Root-Path-Platzhalter
Naming Conventions koennen zusaetzlich Bestandteile des konfigurierten `RootPath` verwenden. Die Platzhalter funktionieren in `NamingTemplate`, `DescriptionTemplate` und `Wildcard`.
Fuer:
```text
RootPath=\\SRVWSM001.imagoverum.com\file_shares\share2
Zielpfad=\\SRVWSM001.imagoverum.com\file_shares\share2\test33
```
stehen folgende Root-Platzhalter zur Verfuegung:
- `{{ROOT_SERVER}}`: Serveranteil, z.B. `SRVWSM001.imagoverum.com`
- `{{ROOT_NAME}}`: letzter Root-Segmentname, z.B. `share2`
- `{{ROOT_PATH}}`: alle Root-Segmente nach dem Server, z.B. `file_shares_share2`
- `{{ROOT_PATH(1)}}`: die letzten `n` Root-Segmente, z.B. `share2`
- `{{ROOT_PATH(2)}}`: z.B. `file_shares_share2`
- `{{ROOT_SEGMENT(0)}}`: erstes Root-Segment nach dem Server, z.B. `file_shares`
- `{{ROOT_SEGMENT(1)}}`: zweites Root-Segment nach dem Server, z.B. `share2`
Root-Segmente werden wie Ordnersegmente sanitisiert. Leerzeichen und Bindestriche werden standardmaessig zu `_`. Nicht vorhandene `ROOT_SEGMENT(n)`-Werte werden zu einem leeren String. Wenn `ROOT_PATH(n)` mehr Segmente anfordert als vorhanden sind, werden alle vorhandenen Root-Segmente verwendet.
Beispiel:
```text
{{ADGroupPrefix}}_{{ROOT_NAME}}.{{NAME}}{{GROUPTYPEPOSTFIX}}
```
ergibt fuer die Owner-Gruppe des Zielordners:
```text
ACL_SHARE2.TEST33_O
```
Alternativ mit Namespace-/Root-Anteil:
```text
{{ADGroupPrefix}}_{{ROOT_PATH(2)}}.{{NAME}}{{GROUPTYPEPOSTFIX}}
```
ergibt:
```text
ACL_FILE_SHARES_SHARE2.TEST33_O
```
Die bestehenden Platzhalter `{{NAME}}`, `{{RELATIVEPATH}}`, `{{TRAVERSE_NAME}}` und `{{TRAVERSE_VISIBLEPATH}}` bleiben unveraendert.
### 11. Konfigurierbares Sanitizing und Gross-/Kleinschreibung fuer NTFS-Gruppennamen
Die Normalisierung der dynamischen Pfadbestandteile wird ueber `AdditionalConfiguration` gesteuert. Die Werte kommen wie `EnsureNtfsPermissionGroups` aus `C4IT_GCC_DataArea_Collector_AdditionalAttributes`.
`NtfsGroupNameSanitizeReplacement` steuert das Ersatz-/Trennzeichen fuer dynamische Pfadbestandteile:
- nicht gesetzt: AD-kritische Zeichen werden durch `_` ersetzt und Pfadsegmente werden mit `_` verbunden
- gesetzt auf z.B. `.`: AD-kritische Zeichen werden durch `.` ersetzt und Pfadsegmente werden mit `.` verbunden
- gesetzt auf einen leeren Wert, `<empty>`, `empty`, `none` oder `remove`: AD-kritische Zeichen werden entfernt und Pfadsegmente ohne Trennzeichen verbunden
Als AD-kritisch gelten in dynamischen Pfadbestandteilen derzeit Whitespace, Bindestrich, Steuerzeichen sowie `/ \ [ ] : ; | = , + * ? < > @ ( ) ' "`. Diese Zeichen sind fuer AD-`sAMAccountName`, LDAP-DNs/Search-Filter oder Entra-/Graph-kompatible Gruppennamen problematisch. `.` und `_` bleiben erhalten.
Die Einstellung wirkt auf `{{NAME}}`, `{{RELATIVEPATH}}`, `{{ROOT_*}}`, `{{TRAVERSE_NAME}}` und `{{TRAVERSE_VISIBLEPATH}}`. Sie aendert nicht die statischen Zeichen, die direkt im Naming Template stehen. Soll z.B. zwischen Root und Ordner immer ein Punkt stehen, bleibt der Punkt Bestandteil des Templates.
Beispiel:
```text
RootPath=\\SRVWSM001.imagoverum.com\file_shares\share2
Zielpfad=\\SRVWSM001.imagoverum.com\file_shares\share2\test-33
NamingTemplate={{ADGroupPrefix}}_{{ROOT_NAME}}.{{NAME}}{{GROUPTYPEPOSTFIX}}
NtfsGroupNameSanitizeReplacement=
```
ergibt bei deaktivierter automatischer Grossschreibung:
```text
ACL_share2.test33_O
```
`PreserveNtfsAdGroupNameCase=1` unterbindet die bisher automatische Grossschreibung der erzeugten AD-Gruppennamen. Ohne diesen Schalter bleibt das bisherige Verhalten erhalten und die generierten CN-/sAMAccountName-Werte werden in Grossbuchstaben erzeugt.
### 12. Domain-Controller-Pinning fuer direkte AD-Readbacks
Der NTFS-Provider pinnt seine AD-Zugriffe auf einen konkreten Domain Controller, damit neu angelegte Gruppen unmittelbar im gleichen oder in einem direkt folgenden Workflow-Lauf wieder gefunden werden koennen. Ohne Pinning kann Windows bei getrennten WF-Instanzen unterschiedliche DCs auswaehlen; bei langen AD-Replikationsintervallen waeren frisch angelegte Gruppen dann auf dem zweiten DC noch nicht sichtbar.
Standardverhalten ohne Konfiguration:
- der Provider ermittelt automatisch den PDC Emulator der Domain
- alle NTFS-AD-Zugriffe innerhalb des Provider-Laufs verwenden diesen DC
- der verwendete DC wird einmal beim AD-Logon im Debug-Log ausgegeben
Optional kann ueber `AdditionalConfiguration` eine priorisierte, kommagetrennte DC-Liste gesetzt werden:
```text
NtfsAdDomainControllers=dc01.imagoverum.com,dc02.imagoverum.com
```
Der Provider testet die Eintraege in Reihenfolge. Der erste erreichbare DC wird verwendet. Wenn kein konfigurierter DC erreichbar ist, faellt der Provider auf den automatisch ermittelten PDC Emulator zurueck. Wenn auch dieser nicht ermittelt werden kann, wird wie bisher die Domain selbst verwendet und damit wieder der Windows-DC-Locator genutzt.
Die gleiche Logik gilt fuer den ActiveDirectory-Provider. Dort koennen die DCs ueber `AdDomainControllers` oder alternativ `ActiveDirectoryDomainControllers` gesetzt werden:
```text
AdDomainControllers=dc01.imagoverum.com,dc02.imagoverum.com
```
Auch hier wird ohne Konfiguration automatisch der PDC Emulator verwendet. Der tatsaechlich verwendete DC wird beim AD-Logon im Debug-Log ausgegeben.
## Matching-Regeln ## Matching-Regeln
Empfohlene Semantik: Empfohlene Semantik:

View File

@@ -37,6 +37,9 @@ Das bedeutet:
- jeder Ordner wird isoliert behandelt - jeder Ordner wird isoliert behandelt
- keine gemeinsame Voranalyse - keine gemeinsame Voranalyse
- kein Caching ueber den gesamten Lauf - kein Caching ueber den gesamten Lauf
- Owner-/Write-/Read-Gruppen sowie Traverse-Gruppen werden analog zur Ordner-Neuanlage sichergestellt
- eine optionale Traverse-Grenze kann ueber `NtfsTraverseBoundaryPath` aus `AdditionalConfiguration` gesetzt werden
- das Sanitizing dynamischer Pfadbestandteile kann ueber `NtfsGroupNameSanitizeReplacement` angepasst werden; `PreserveNtfsAdGroupNameCase=1` unterbindet die automatische Grossschreibung neuer AD-Gruppennamen
### 2. Create-/Ensure-Pfad ### 2. Create-/Ensure-Pfad

View File

@@ -0,0 +1,265 @@
<#
.SYNOPSIS
Creates or uses a dedicated LIAM test account and delegates limited AD and NTFS rights for validation.
.DESCRIPTION
Use this script to verify whether the reduced LIAM service-user permissions are sufficient.
Run with -WhatIf first, then without -WhatIf, configure the account in LIAM, and execute the relevant workflows.
Requirements:
- RSAT ActiveDirectory module
- Run as an account allowed to create users and edit ACLs on the target OU and NTFS paths
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory = $true)]
[string]$SamAccountName,
[Parameter(Mandatory = $true)]
[string]$TargetGroupOuDN,
[string[]]$ReadSearchBaseDNs = @(),
[string[]]$NtfsReadAclPaths = @(),
[string[]]$NtfsManageAclPaths = @(),
[string[]]$NtfsCreateParentPaths = @(),
[switch]$CreateUser,
[switch]$GrantDeleteGroupObjects
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
Import-Module ActiveDirectory -ErrorAction Stop
function Get-SchemaGuid {
param(
[Parameter(Mandatory = $true)]
[string]$LdapDisplayName
)
$schemaNc = (Get-ADRootDSE).schemaNamingContext
$object = Get-ADObject -SearchBase $schemaNc `
-LDAPFilter "(lDAPDisplayName=$LdapDisplayName)" `
-Properties schemaIDGUID
if (-not $object) {
throw "Schema object not found: $LdapDisplayName"
}
return [Guid]::new([byte[]]$object.schemaIDGUID)
}
function Add-LiamAdAccessRule {
param(
[Parameter(Mandatory = $true)]
[string]$TargetDN,
[Parameter(Mandatory = $true)]
[System.Security.Principal.IdentityReference]$Identity,
[Parameter(Mandatory = $true)]
[System.DirectoryServices.ActiveDirectoryRights]$Rights,
[Parameter(Mandatory = $true)]
[System.Security.AccessControl.AccessControlType]$AccessType,
[Guid]$ObjectType = [Guid]::Empty,
[System.DirectoryServices.ActiveDirectorySecurityInheritance]$Inheritance = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::None,
[Guid]$InheritedObjectType = [Guid]::Empty
)
$entry = [ADSI]"LDAP://$TargetDN"
$acl = $entry.ObjectSecurity
if ($InheritedObjectType -ne [Guid]::Empty) {
$rule = [System.DirectoryServices.ActiveDirectoryAccessRule]::new(
$Identity,
$Rights,
$AccessType,
$ObjectType,
$Inheritance,
$InheritedObjectType
)
}
elseif ($ObjectType -ne [Guid]::Empty) {
$rule = [System.DirectoryServices.ActiveDirectoryAccessRule]::new(
$Identity,
$Rights,
$AccessType,
$ObjectType
)
}
else {
$rule = [System.DirectoryServices.ActiveDirectoryAccessRule]::new(
$Identity,
$Rights,
$AccessType,
$Inheritance
)
}
$acl.AddAccessRule($rule)
if ($PSCmdlet.ShouldProcess($TargetDN, "Add AD ACL: $Rights / $ObjectType / $Inheritance")) {
$entry.ObjectSecurity = $acl
$entry.CommitChanges()
}
}
function Grant-LiamNtfsRights {
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[string]$Account,
[Parameter(Mandatory = $true)]
[System.Security.AccessControl.FileSystemRights]$Rights,
[System.Security.AccessControl.InheritanceFlags]$InheritanceFlags = "ContainerInherit,ObjectInherit",
[System.Security.AccessControl.PropagationFlags]$PropagationFlags = "None"
)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Path not found: $Path"
}
$acl = Get-Acl -LiteralPath $Path
$rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
$Account,
$Rights,
$InheritanceFlags,
$PropagationFlags,
[System.Security.AccessControl.AccessControlType]::Allow
)
$acl.AddAccessRule($rule)
if ($PSCmdlet.ShouldProcess($Path, "Grant NTFS rights '$Rights' to '$Account'")) {
Set-Acl -LiteralPath $Path -AclObject $acl
}
}
$domain = Get-ADDomain
$netbiosName = $domain.NetBIOSName
$accountName = "$netbiosName\$SamAccountName"
if ($CreateUser) {
$existingUser = Get-ADUser -LDAPFilter "(sAMAccountName=$SamAccountName)" -ErrorAction SilentlyContinue
if (-not $existingUser) {
$password = Read-Host "Password for $SamAccountName" -AsSecureString
if ($PSCmdlet.ShouldProcess($SamAccountName, "Create AD user")) {
New-ADUser `
-SamAccountName $SamAccountName `
-Name $SamAccountName `
-AccountPassword $password `
-Enabled $true `
-PasswordNeverExpires $true `
-ChangePasswordAtLogon $false
}
}
}
$user = Get-ADUser -Identity $SamAccountName
$identity = $user.SID
$groupClassGuid = Get-SchemaGuid "group"
$attributeNames = @(
"cn",
"sAMAccountName",
"displayName",
"groupType",
"description",
"managedBy",
"member"
)
$attributeGuids = @{}
foreach ($name in $attributeNames) {
$attributeGuids[$name] = Get-SchemaGuid $name
}
Add-LiamAdAccessRule `
-TargetDN $TargetGroupOuDN `
-Identity $identity `
-Rights ([System.DirectoryServices.ActiveDirectoryRights]"ListChildren,ReadProperty") `
-AccessType Allow `
-Inheritance All
Add-LiamAdAccessRule `
-TargetDN $TargetGroupOuDN `
-Identity $identity `
-Rights CreateChild `
-AccessType Allow `
-ObjectType $groupClassGuid
if ($GrantDeleteGroupObjects) {
Add-LiamAdAccessRule `
-TargetDN $TargetGroupOuDN `
-Identity $identity `
-Rights DeleteChild `
-AccessType Allow `
-ObjectType $groupClassGuid
}
foreach ($attributeName in $attributeNames) {
Add-LiamAdAccessRule `
-TargetDN $TargetGroupOuDN `
-Identity $identity `
-Rights WriteProperty `
-AccessType Allow `
-ObjectType $attributeGuids[$attributeName] `
-Inheritance Descendents `
-InheritedObjectType $groupClassGuid
}
foreach ($readBase in $ReadSearchBaseDNs) {
Add-LiamAdAccessRule `
-TargetDN $readBase `
-Identity $identity `
-Rights ([System.DirectoryServices.ActiveDirectoryRights]"ListChildren,ReadProperty") `
-AccessType Allow `
-Inheritance All
}
foreach ($path in $NtfsReadAclPaths) {
Grant-LiamNtfsRights `
-Path $path `
-Account $accountName `
-Rights ([System.Security.AccessControl.FileSystemRights]"ReadAndExecute,ReadPermissions")
}
foreach ($path in $NtfsManageAclPaths) {
Grant-LiamNtfsRights `
-Path $path `
-Account $accountName `
-Rights ([System.Security.AccessControl.FileSystemRights]"ReadAndExecute,ReadPermissions,ChangePermissions")
}
foreach ($path in $NtfsCreateParentPaths) {
Grant-LiamNtfsRights `
-Path $path `
-Account $accountName `
-Rights ([System.Security.AccessControl.FileSystemRights]"ReadAndExecute,ReadPermissions,CreateDirectories") `
-InheritanceFlags None `
-PropagationFlags None
}
Write-Host "Delegation finished for $accountName"
Write-Host "Suggested validation:"
Write-Host "1. Configure this account as the LIAM provider credential."
Write-Host "2. Create AD service groups and add members."
Write-Host "3. Read NTFS data areas."
Write-Host "4. Ensure missing NTFS permission groups and ACL entries."
Write-Host "5. Verify that AD deletes and writes outside the target OU fail."