821 lines
34 KiB
C#
821 lines
34 KiB
C#
using C4IT_IAM_GET;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.DirectoryServices;
|
|
using System.DirectoryServices.AccountManagement;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Security;
|
|
using System.Security.AccessControl;
|
|
using System.Security.Principal;
|
|
using System.Text.RegularExpressions;
|
|
using System.Xml.Serialization;
|
|
|
|
using static C4IT.Logging.cLogManager;
|
|
using System.Reflection;
|
|
using C4IT.Logging;
|
|
|
|
namespace C4IT_IAM_Engine
|
|
{
|
|
public class SecurityGroups
|
|
{
|
|
public string domainName;
|
|
public string username;
|
|
public SecureString password;
|
|
public bool ForceStrictAdGroupNames;
|
|
|
|
public List<IAM_SecurityGroup> IAM_SecurityGroups;
|
|
public string rootUID;
|
|
public SecurityGroups()
|
|
{
|
|
IAM_SecurityGroups = new List<IAM_SecurityGroup>();
|
|
}
|
|
public bool GroupsAllreadyExisting(string ouPath)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
|
|
try
|
|
{
|
|
|
|
int groupCount = 0;
|
|
if (IAM_SecurityGroups != null)
|
|
foreach (var s in IAM_SecurityGroups)
|
|
{
|
|
if (s.securityGroupType != SecurityGroupType.Traverse)
|
|
{
|
|
DirectoryEntry entry = new DirectoryEntry
|
|
{
|
|
Path = "LDAP://" + domainName,
|
|
Username = username,
|
|
Password = new NetworkCredential("", password).Password,
|
|
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
|
|
};
|
|
DirectorySearcher dSearch = new DirectorySearcher(entry)
|
|
{
|
|
Filter = "(&(CN=" + s.Name.ToUpper() + ")(objectClass=group))"
|
|
};
|
|
dSearch.PageSize = 100000;
|
|
SearchResultCollection sr = dSearch.FindAll();
|
|
groupCount += sr.Count;
|
|
}
|
|
}
|
|
return groupCount > 0;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
}
|
|
|
|
public bool GroupAllreadyExisting(string CN)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
|
|
try
|
|
{
|
|
|
|
int groupCount = 0;
|
|
if (CN != string.Empty)
|
|
{
|
|
DirectoryEntry entry = new DirectoryEntry
|
|
{
|
|
Path = "LDAP://" + domainName,
|
|
Username = username,
|
|
Password = new NetworkCredential("", password).Password,
|
|
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
|
|
};
|
|
DirectorySearcher dSearch = new DirectorySearcher(entry)
|
|
{
|
|
Filter = "(&(CN=" + CN.ToUpper() + ")(objectClass=group))"
|
|
};
|
|
dSearch.PageSize = 100000;
|
|
SearchResultCollection sr = dSearch.FindAll();
|
|
groupCount += sr.Count;
|
|
}
|
|
return groupCount > 0;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
}
|
|
|
|
public void GenerateNewSecurityGroups(
|
|
string baseFolder,
|
|
string newFolderPath,
|
|
string groupPrefix,
|
|
string ouPath,
|
|
PermissionGroupStrategy groupPermissionStrategy,
|
|
string groupTraverseTag,
|
|
string groupReadTag,
|
|
string groupWriteTag,
|
|
string groupOwnerTag,
|
|
string groupDLTag,
|
|
string groupGTag,
|
|
IDictionary<string, string> customTags,
|
|
List<IAM_SecurityGroupTemplate> templates,
|
|
int readACLPermission,
|
|
int writeACLPermission,
|
|
int ownerACLPermission,
|
|
int loop = 0,
|
|
int existingADGroupCount = 0)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
try
|
|
{
|
|
var resolvedTemplates = (templates ?? new List<IAM_SecurityGroupTemplate>())
|
|
.Select(template => new IAM_SecurityGroupTemplate(
|
|
template.NamingTemplate,
|
|
template.DescriptionTemplate,
|
|
template.WildcardTemplate,
|
|
template.Type,
|
|
template.Scope))
|
|
.ToList();
|
|
var relativePathRaw = DataArea.GetRelativePath(newFolderPath, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
|
relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
|
var relativePathSegments = relativePathRaw.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
|
|
var sanitizedSegments = relativePathSegments.Select(Helper.SanitizePathSegment).ToArray();
|
|
var relativePath = sanitizedSegments.Length > 0 ? string.Join("_", sanitizedSegments) : string.Empty;
|
|
var folderName = sanitizedSegments.Length > 0
|
|
? sanitizedSegments[sanitizedSegments.Length - 1]
|
|
: Helper.SanitizePathSegment(Path.GetFileName(newFolderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)));
|
|
|
|
foreach (var template in resolvedTemplates)
|
|
{
|
|
var GroupTypeTag = "";
|
|
switch (template.Type)
|
|
{
|
|
case SecurityGroupType.Owner:
|
|
GroupTypeTag = groupOwnerTag;
|
|
break;
|
|
case SecurityGroupType.Write:
|
|
GroupTypeTag = groupWriteTag;
|
|
break;
|
|
case SecurityGroupType.Read:
|
|
GroupTypeTag = groupReadTag;
|
|
break;
|
|
case SecurityGroupType.Traverse:
|
|
GroupTypeTag = groupTraverseTag;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
var GroupScopeTag = "";
|
|
switch (template.Scope)
|
|
{
|
|
case GroupScope.Global:
|
|
GroupScopeTag = groupGTag;
|
|
break;
|
|
case GroupScope.Local:
|
|
GroupScopeTag = groupDLTag;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
var tags = new Dictionary<string, string>();
|
|
tags.Add("PREFIX", groupPrefix);
|
|
tags.Add("GROUPTYPEPOSTFIX", GroupTypeTag);
|
|
tags.Add("SCOPETAG", GroupScopeTag);
|
|
|
|
template.NamingTemplate = Helper.ApplyTemplatePlaceholders(template.NamingTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
|
.ReplaceTags(customTags).ReplaceTags(tags)
|
|
.ToUpper();
|
|
|
|
template.DescriptionTemplate = Helper.ApplyTemplatePlaceholders(template.DescriptionTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
|
.ReplaceTags(customTags).ReplaceTags(tags)
|
|
.ToUpper();
|
|
|
|
|
|
template.WildcardTemplate = Helper.ApplyTemplatePlaceholders(template.WildcardTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
|
.ReplaceTags(customTags).ReplaceTags(tags)
|
|
.ToUpper();
|
|
|
|
}
|
|
|
|
IAM_SecurityGroupTemplate ownerGlobal = resolvedTemplates.First(t => t.Scope.Equals(GroupScope.Global) && t.Type.Equals(SecurityGroupType.Owner));
|
|
IAM_SecurityGroup osecGroup = new IAM_SecurityGroup()
|
|
{
|
|
Name = ownerGlobal.NamingTemplate,
|
|
description = ownerGlobal.DescriptionTemplate,
|
|
WildcardPattern = ownerGlobal.WildcardTemplate,
|
|
|
|
technicalName = "CN=" + ownerGlobal.NamingTemplate + "," + ouPath,
|
|
targetTyp = (int)IAM_TargetType.FileSystem,
|
|
rights = (FileSystemRights)ownerACLPermission,
|
|
Scope = GroupScope.Global
|
|
};
|
|
IAM_SecurityGroups.Add(osecGroup);
|
|
|
|
IAM_SecurityGroupTemplate writeGlobal = resolvedTemplates.First(t => t.Scope.Equals(GroupScope.Global) && t.Type.Equals(SecurityGroupType.Write));
|
|
IAM_SecurityGroup wsecGroup = new IAM_SecurityGroup()
|
|
{
|
|
Name = writeGlobal.NamingTemplate,
|
|
description = writeGlobal.DescriptionTemplate,
|
|
WildcardPattern = writeGlobal.WildcardTemplate,
|
|
|
|
technicalName = "CN=" + writeGlobal.NamingTemplate + "," + ouPath,
|
|
targetTyp = (int)IAM_TargetType.FileSystem,
|
|
rights = (FileSystemRights)writeACLPermission,
|
|
Scope = GroupScope.Global
|
|
};
|
|
IAM_SecurityGroups.Add(wsecGroup);
|
|
|
|
IAM_SecurityGroupTemplate readGlobal = resolvedTemplates.First(t => t.Scope.Equals(GroupScope.Global) && t.Type.Equals(SecurityGroupType.Read));
|
|
IAM_SecurityGroup rsecGroup = new IAM_SecurityGroup()
|
|
{
|
|
Name = readGlobal.NamingTemplate,
|
|
description = readGlobal.DescriptionTemplate,
|
|
WildcardPattern = readGlobal.WildcardTemplate,
|
|
|
|
technicalName = "CN=" + readGlobal.NamingTemplate + "," + ouPath,
|
|
targetTyp = (int)IAM_TargetType.FileSystem,
|
|
rights = (FileSystemRights)readACLPermission,
|
|
Scope = GroupScope.Global
|
|
};
|
|
IAM_SecurityGroups.Add(rsecGroup);
|
|
|
|
|
|
//
|
|
if (groupPermissionStrategy == PermissionGroupStrategy.AGDLP)
|
|
{
|
|
IAM_SecurityGroupTemplate ownerDL = resolvedTemplates.First(t => t.Scope.Equals(GroupScope.Local) && t.Type.Equals(SecurityGroupType.Owner));
|
|
IAM_SecurityGroup osecDLGroup = new IAM_SecurityGroup()
|
|
{
|
|
Name = ownerDL.NamingTemplate,
|
|
description = ownerDL.DescriptionTemplate,
|
|
WildcardPattern = ownerDL.WildcardTemplate,
|
|
|
|
technicalName = "CN=" + ownerDL.NamingTemplate + "," + ouPath,
|
|
targetTyp = (int)IAM_TargetType.FileSystem,
|
|
rights = (FileSystemRights)ownerACLPermission,
|
|
Scope = GroupScope.Local
|
|
};
|
|
osecDLGroup.memberGroups.Add(osecGroup);
|
|
IAM_SecurityGroups.Add(osecDLGroup);
|
|
|
|
IAM_SecurityGroupTemplate writeDL = resolvedTemplates.First(t => t.Scope.Equals(GroupScope.Local) && t.Type.Equals(SecurityGroupType.Write));
|
|
IAM_SecurityGroup wsecDLGroup = new IAM_SecurityGroup()
|
|
{
|
|
Name = writeDL.NamingTemplate,
|
|
description = writeDL.DescriptionTemplate,
|
|
WildcardPattern = writeDL.WildcardTemplate,
|
|
|
|
technicalName = "CN=" + writeDL.NamingTemplate + "," + ouPath,
|
|
targetTyp = (int)IAM_TargetType.FileSystem,
|
|
rights = (FileSystemRights)writeACLPermission,
|
|
Scope = GroupScope.Local
|
|
};
|
|
wsecDLGroup.memberGroups.Add(wsecGroup);
|
|
IAM_SecurityGroups.Add(wsecDLGroup);
|
|
|
|
IAM_SecurityGroupTemplate readDL = resolvedTemplates.First(t => t.Scope.Equals(GroupScope.Local) && t.Type.Equals(SecurityGroupType.Read));
|
|
IAM_SecurityGroup rsecDLGroup = new IAM_SecurityGroup()
|
|
{
|
|
Name = readDL.NamingTemplate,
|
|
description = readDL.DescriptionTemplate,
|
|
WildcardPattern = readDL.WildcardTemplate,
|
|
|
|
technicalName = "CN=" + readDL.NamingTemplate + "," + ouPath,
|
|
targetTyp = (int)IAM_TargetType.FileSystem,
|
|
rights = (FileSystemRights)readACLPermission,
|
|
Scope = GroupScope.Local
|
|
};
|
|
rsecDLGroup.memberGroups.Add(rsecGroup);
|
|
IAM_SecurityGroups.Add(rsecDLGroup);
|
|
}
|
|
foreach (var secGroup in IAM_SecurityGroups)
|
|
{
|
|
secGroup.description = secGroup.description.ReplaceLoopTag(0);
|
|
secGroup.Name = secGroup.Name.ReplaceLoopTag(loop);
|
|
secGroup.technicalName = secGroup.technicalName.ReplaceLoopTag(loop);
|
|
secGroup.WildcardPattern = secGroup.WildcardPattern.ReplaceLoopTag(loop);
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Security group generated: {secGroup.technicalName}");
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static string GetRightPartOfPath(string path, string startAfterPart)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
try
|
|
{
|
|
// use the correct seperator for the environment
|
|
var pathParts = path.Split(Path.DirectorySeparatorChar);
|
|
if (startAfterPart.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
|
{
|
|
startAfterPart = startAfterPart.Substring(0, startAfterPart.Length - 1);
|
|
}
|
|
var startAfter = startAfterPart.Split(Path.DirectorySeparatorChar);
|
|
string newPath = String.Empty;
|
|
if (pathParts.Length > startAfter.Length)
|
|
{
|
|
for (int i = startAfter.Length; pathParts.Length > i; i++)
|
|
{
|
|
newPath += pathParts[i] + Path.DirectorySeparatorChar;
|
|
}
|
|
}
|
|
if (newPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
|
{
|
|
newPath = newPath.Substring(0, newPath.Length - 1);
|
|
}
|
|
|
|
// try and work out if last part was a directory - if not, drop the last part as we don't want the filename
|
|
return newPath;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
}
|
|
|
|
public static string getSID(DirectoryEntry ent)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
try
|
|
{
|
|
var usrId = (byte[])ent.Properties["objectSid"][0];
|
|
return (new SecurityIdentifier(usrId, 0)).ToString();
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
|
|
}
|
|
|
|
private DirectoryEntry FindGroupEntry(string groupName)
|
|
{
|
|
DirectoryEntry entry = new DirectoryEntry
|
|
{
|
|
Path = "LDAP://" + domainName,
|
|
Username = username,
|
|
Password = new NetworkCredential("", password).Password,
|
|
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
|
|
};
|
|
|
|
DirectorySearcher search = new DirectorySearcher(entry)
|
|
{
|
|
Filter = "(&(objectClass=group)(sAMAccountName=" + groupName.ToUpper() + "))"
|
|
};
|
|
search.PageSize = 100000;
|
|
|
|
var result = search.FindOne();
|
|
if (result == null)
|
|
{
|
|
entry.Dispose();
|
|
search.Dispose();
|
|
return null;
|
|
}
|
|
|
|
var groupEntry = result.GetDirectoryEntry();
|
|
search.Dispose();
|
|
entry.Dispose();
|
|
return groupEntry;
|
|
}
|
|
|
|
private DirectoryEntry FindGroupEntryByWildcard(string ouPath, string wildcardPattern)
|
|
{
|
|
if (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://" + domainName;
|
|
if (!string.IsNullOrWhiteSpace(ouPath))
|
|
basePath += "/" + ouPath;
|
|
|
|
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("distinguishedName");
|
|
|
|
string matchedName = null;
|
|
string matchedDistinguishedName = 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 wildcard '{wildcardPattern}' in '{basePath}'. Regex-based reuse is skipped.");
|
|
search.Dispose();
|
|
entry.Dispose();
|
|
return null;
|
|
}
|
|
|
|
matchedName = samAccountName;
|
|
matchedDistinguishedName = result.Properties.Contains("distinguishedName") && result.Properties["distinguishedName"].Count > 0
|
|
? result.Properties["distinguishedName"][0]?.ToString()
|
|
: null;
|
|
}
|
|
|
|
search.Dispose();
|
|
entry.Dispose();
|
|
|
|
if (string.IsNullOrWhiteSpace(matchedDistinguishedName))
|
|
return null;
|
|
|
|
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);
|
|
}
|
|
|
|
private DirectoryEntry FindGroupEntryFromFolderAcl(string folderPath, string wildcardPattern)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(folderPath) || string.IsNullOrWhiteSpace(wildcardPattern) || !Directory.Exists(folderPath))
|
|
return null;
|
|
|
|
Regex wildcardRegex;
|
|
try
|
|
{
|
|
wildcardRegex = new Regex(wildcardPattern, RegexOptions.IgnoreCase);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
var directory = new DirectoryInfo(folderPath);
|
|
var rules = directory.GetAccessControl(AccessControlSections.Access)
|
|
.GetAccessRules(true, false, typeof(SecurityIdentifier))
|
|
.Cast<FileSystemAccessRule>();
|
|
var matchedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
using (var domainContext = new PrincipalContext(ContextType.Domain, domainName, username, new NetworkCredential("", password).Password))
|
|
{
|
|
foreach (var rule in rules)
|
|
{
|
|
if (rule.AccessControlType != AccessControlType.Allow)
|
|
continue;
|
|
|
|
var sid = rule.IdentityReference?.Value;
|
|
if (string.IsNullOrWhiteSpace(sid) || sid == "S-1-1-0")
|
|
continue;
|
|
|
|
using (var group = GroupPrincipal.FindByIdentity(domainContext, IdentityType.Sid, sid))
|
|
{
|
|
var samAccountName = group?.SamAccountName;
|
|
if (string.IsNullOrWhiteSpace(samAccountName) || !wildcardRegex.IsMatch(samAccountName))
|
|
continue;
|
|
|
|
matchedNames.Add(samAccountName);
|
|
if (matchedNames.Count > 1)
|
|
{
|
|
DefaultLogger.LogEntry(LogLevels.Warning, $"Multiple ACL groups on folder '{folderPath}' matched wildcard '{wildcardPattern}'. ACL-based reuse is skipped.");
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matchedNames.Count == 0)
|
|
return null;
|
|
|
|
var matchedName = matchedNames.First();
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Reusing ACL-linked AD group '{matchedName}' via wildcard '{wildcardPattern}' on folder '{folderPath}'.");
|
|
return FindGroupEntry(matchedName);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void ApplyExistingGroup(IAM_SecurityGroup secGroup, DirectoryEntry existingGroup)
|
|
{
|
|
secGroup.CreatedNewEntry = false;
|
|
secGroup.UID = getSID(existingGroup);
|
|
|
|
if (existingGroup.Properties.Contains("sAMAccountName") && existingGroup.Properties["sAMAccountName"].Count > 0)
|
|
secGroup.Name = existingGroup.Properties["sAMAccountName"][0]?.ToString();
|
|
|
|
if (existingGroup.Properties.Contains("distinguishedName") && existingGroup.Properties["distinguishedName"].Count > 0)
|
|
secGroup.technicalName = existingGroup.Properties["distinguishedName"][0]?.ToString();
|
|
}
|
|
|
|
private static bool HasMember(PropertyValueCollection members, string distinguishedName)
|
|
{
|
|
foreach (var member in members)
|
|
{
|
|
if (string.Equals(member?.ToString(), distinguishedName, StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void AddMissingMembers(DirectoryEntry group, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
|
{
|
|
if (group == null)
|
|
return;
|
|
|
|
var changed = false;
|
|
if (users != null && secGroup.Scope == GroupScope.Global)
|
|
{
|
|
foreach (var user in users.Where(u => u != null && !string.IsNullOrWhiteSpace(u.DistinguishedName)))
|
|
{
|
|
if (HasMember(group.Properties["member"], user.DistinguishedName))
|
|
continue;
|
|
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing member: {user.DistinguishedName}");
|
|
group.Properties["member"].Add(user.DistinguishedName);
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (secGroup.Scope == GroupScope.Local)
|
|
{
|
|
foreach (var innerGroup in secGroup.memberGroups.Where(g => g != null && !string.IsNullOrWhiteSpace(g.technicalName)))
|
|
{
|
|
if (HasMember(group.Properties["member"], innerGroup.technicalName))
|
|
continue;
|
|
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing nested group: {innerGroup.technicalName}");
|
|
group.Properties["member"].Add(innerGroup.technicalName);
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (changed)
|
|
group.CommitChanges();
|
|
}
|
|
|
|
public DirectoryEntry EnsureADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users, string folderPath = null)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
try
|
|
{
|
|
secGroup.CreatedNewEntry = false;
|
|
DirectoryEntry existingGroup = null;
|
|
if (!ForceStrictAdGroupNames)
|
|
existingGroup = FindGroupEntryFromFolderAcl(folderPath, secGroup.WildcardPattern);
|
|
|
|
if (existingGroup == null)
|
|
existingGroup = FindGroupEntry(secGroup.Name);
|
|
|
|
if (existingGroup == null && !ForceStrictAdGroupNames && string.IsNullOrWhiteSpace(folderPath))
|
|
existingGroup = FindGroupEntryByWildcard(ouPath, secGroup.WildcardPattern);
|
|
|
|
if (existingGroup == null)
|
|
return CreateADGroup(ouPath, secGroup, users);
|
|
|
|
AddMissingMembers(existingGroup, secGroup, users);
|
|
ApplyExistingGroup(secGroup, existingGroup);
|
|
return existingGroup;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
}
|
|
|
|
public DirectoryEntry PreviewADGroup(string ouPath, IAM_SecurityGroup secGroup, string folderPath = null)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
try
|
|
{
|
|
secGroup.CreatedNewEntry = false;
|
|
DirectoryEntry existingGroup = null;
|
|
if (!ForceStrictAdGroupNames)
|
|
existingGroup = FindGroupEntryFromFolderAcl(folderPath, secGroup.WildcardPattern);
|
|
|
|
if (existingGroup == null)
|
|
existingGroup = FindGroupEntry(secGroup.Name);
|
|
|
|
if (existingGroup == null && !ForceStrictAdGroupNames && string.IsNullOrWhiteSpace(folderPath))
|
|
existingGroup = FindGroupEntryByWildcard(ouPath, secGroup.WildcardPattern);
|
|
|
|
if (existingGroup == null)
|
|
return null;
|
|
|
|
ApplyExistingGroup(secGroup, existingGroup);
|
|
return existingGroup;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
}
|
|
|
|
public DirectoryEntry CreateADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
|
{
|
|
LogMethodBegin(MethodBase.GetCurrentMethod());
|
|
try
|
|
{
|
|
secGroup.CreatedNewEntry = false;
|
|
if (!GroupAllreadyExisting(secGroup.Name.ToUpper()))
|
|
{
|
|
|
|
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domainName + "/" + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Creating ad entry with CN / sAmAccountName: {secGroup.Name.ToUpper()}");
|
|
DirectoryEntry group = entry.Children.Add("CN=" + secGroup.Name.ToUpper(), "group");
|
|
group.Properties["sAmAccountName"].Value = secGroup.Name.ToUpper();
|
|
if (users != null && secGroup.Scope == GroupScope.Global)
|
|
{
|
|
foreach (var user in users)
|
|
{
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding member: {user.DistinguishedName}");
|
|
group.Properties["member"].Add(user.DistinguishedName);
|
|
}
|
|
}
|
|
if(!String.IsNullOrEmpty(secGroup.description))
|
|
{
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Setting description: {secGroup.description}");
|
|
group.Properties["description"].Value = secGroup.description;
|
|
}
|
|
var groupType = secGroup.Scope == GroupScope.Global ? GroupScopeValues.Global : GroupScopeValues.Local;
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Setting groupType to: {groupType}");
|
|
group.Properties["groupType"].Value = groupType;
|
|
if (secGroup.Scope == GroupScope.Local)
|
|
foreach (var iGroup in secGroup.memberGroups)
|
|
{
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding member: {iGroup.technicalName}");
|
|
group.Properties["member"].Add(iGroup.technicalName);
|
|
}
|
|
|
|
group.CommitChanges();
|
|
DirectoryEntry ent = new DirectoryEntry("LDAP://" + domainName + "/" + "CN =" + secGroup.Name.ToUpper() + "," + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
|
|
|
var objectid = SecurityGroups.getSID(ent);
|
|
DefaultLogger.LogEntry(LogLevels.Debug, $"Security group created in ad: {secGroup.technicalName}");
|
|
secGroup.UID = objectid;
|
|
secGroup.CreatedNewEntry = true;
|
|
return ent;
|
|
}
|
|
else
|
|
{
|
|
DirectoryEntry e = FindGroupEntry(secGroup.Name);
|
|
if (e == null)
|
|
return null;
|
|
AddMissingMembers(e, secGroup, users);
|
|
ApplyExistingGroup(secGroup, e);
|
|
return e;
|
|
}
|
|
return null;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
LogMethodEnd(MethodBase.GetCurrentMethod());
|
|
}
|
|
}
|
|
}
|
|
public enum GroupScopeValues : int
|
|
{
|
|
Global = -2147483646,
|
|
Local = -2147483644
|
|
}
|
|
public class IAM_SecurityGroupTemplate
|
|
{
|
|
private string namingTemplate;
|
|
private string descriptionTemplate;
|
|
private string wildcardTemplate;
|
|
private SecurityGroupType type;
|
|
private GroupScope scope;
|
|
|
|
public IAM_SecurityGroupTemplate(string namingTemplate, string descriptionTemplate, string wildcardTemplate, SecurityGroupType type, GroupScope scope)
|
|
{
|
|
NamingTemplate = namingTemplate;
|
|
DescriptionTemplate = descriptionTemplate;
|
|
WildcardTemplate = wildcardTemplate;
|
|
Type = type;
|
|
Scope = scope;
|
|
}
|
|
|
|
public string NamingTemplate
|
|
{
|
|
get => namingTemplate; set
|
|
{
|
|
namingTemplate = value == null ? "" : value;
|
|
}
|
|
}
|
|
public string DescriptionTemplate
|
|
{
|
|
get => descriptionTemplate; set
|
|
{
|
|
descriptionTemplate = value == null ? "" : value;
|
|
}
|
|
}
|
|
public string WildcardTemplate
|
|
{
|
|
get => wildcardTemplate; set
|
|
{
|
|
wildcardTemplate = value == null ? "" : value;
|
|
}
|
|
}
|
|
public SecurityGroupType Type { get => type; set => type = value; }
|
|
public GroupScope Scope { get => scope; set => scope = value; }
|
|
}
|
|
public class IAM_SecurityGroup
|
|
{
|
|
|
|
public string UID;
|
|
public string Parent = "";
|
|
public string description;
|
|
public string WildcardPattern;
|
|
public bool CreatedNewEntry;
|
|
public List<IAM_SecurityGroup> memberGroups;
|
|
public string Name;
|
|
public string technicalName;
|
|
public SecurityGroupType securityGroupType;
|
|
public int targetTyp;
|
|
public GroupScope Scope;
|
|
public FileSystemRights rights;
|
|
public IAM_SecurityGroup()
|
|
{
|
|
memberGroups = new List<IAM_SecurityGroup>();
|
|
}
|
|
}
|
|
public enum SecurityGroupType
|
|
{
|
|
[XmlEnum(Name = "0")]
|
|
Owner,
|
|
[XmlEnum(Name = "1")]
|
|
Write,
|
|
[XmlEnum(Name = "2")]
|
|
Read,
|
|
[XmlEnum(Name = "3")]
|
|
Traverse
|
|
}
|
|
|
|
|
|
}
|