Files
LIAM/LiamNtfs/C4IT_IAM_SET/SecurityGroup.cs

610 lines
25 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 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 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 templates)
{
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 = templates.First(t => t.Scope.Equals(GroupScope.Global) && t.Type.Equals(SecurityGroupType.Owner));
IAM_SecurityGroup osecGroup = new IAM_SecurityGroup()
{
Name = ownerGlobal.NamingTemplate,
description = ownerGlobal.DescriptionTemplate,
technicalName = "CN=" + ownerGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
rights = (FileSystemRights)ownerACLPermission,
Scope = GroupScope.Global
};
IAM_SecurityGroups.Add(osecGroup);
IAM_SecurityGroupTemplate writeGlobal = templates.First(t => t.Scope.Equals(GroupScope.Global) && t.Type.Equals(SecurityGroupType.Write));
IAM_SecurityGroup wsecGroup = new IAM_SecurityGroup()
{
Name = writeGlobal.NamingTemplate,
description = writeGlobal.DescriptionTemplate,
technicalName = "CN=" + writeGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
rights = (FileSystemRights)writeACLPermission,
Scope = GroupScope.Global
};
IAM_SecurityGroups.Add(wsecGroup);
IAM_SecurityGroupTemplate readGlobal = templates.First(t => t.Scope.Equals(GroupScope.Global) && t.Type.Equals(SecurityGroupType.Read));
IAM_SecurityGroup rsecGroup = new IAM_SecurityGroup()
{
Name = readGlobal.NamingTemplate,
description = readGlobal.DescriptionTemplate,
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 = templates.First(t => t.Scope.Equals(GroupScope.Local) && t.Type.Equals(SecurityGroupType.Owner));
IAM_SecurityGroup osecDLGroup = new IAM_SecurityGroup()
{
Name = ownerDL.NamingTemplate,
description = ownerDL.DescriptionTemplate,
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 = templates.First(t => t.Scope.Equals(GroupScope.Local) && t.Type.Equals(SecurityGroupType.Write));
IAM_SecurityGroup wsecDLGroup = new IAM_SecurityGroup()
{
Name = writeDL.NamingTemplate,
description = writeDL.DescriptionTemplate,
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 = templates.First(t => t.Scope.Equals(GroupScope.Local) && t.Type.Equals(SecurityGroupType.Read));
IAM_SecurityGroup rsecDLGroup = new IAM_SecurityGroup()
{
Name = readDL.NamingTemplate,
description = readDL.DescriptionTemplate,
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);
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 static bool HasMember(PropertyValueCollection members, string distinguishedName)
{
foreach (var member in members)
{
if (string.Equals(member?.ToString(), distinguishedName, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
private void AddMissingMembers(DirectoryEntry group, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
{
if (group == null)
return;
var changed = false;
if (users != null && secGroup.Scope == GroupScope.Global)
{
foreach (var user in users.Where(u => u != null && !string.IsNullOrWhiteSpace(u.DistinguishedName)))
{
if (HasMember(group.Properties["member"], user.DistinguishedName))
continue;
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing member: {user.DistinguishedName}");
group.Properties["member"].Add(user.DistinguishedName);
changed = true;
}
}
if (secGroup.Scope == GroupScope.Local)
{
foreach (var innerGroup in secGroup.memberGroups.Where(g => g != null && !string.IsNullOrWhiteSpace(g.technicalName)))
{
if (HasMember(group.Properties["member"], innerGroup.technicalName))
continue;
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing nested group: {innerGroup.technicalName}");
group.Properties["member"].Add(innerGroup.technicalName);
changed = true;
}
}
if (changed)
group.CommitChanges();
}
public DirectoryEntry EnsureADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
var existingGroup = FindGroupEntry(secGroup.Name);
if (existingGroup == null)
return CreateADGroup(ouPath, secGroup, users);
AddMissingMembers(existingGroup, secGroup, users);
var objectid = getSID(existingGroup);
secGroup.UID = objectid;
return existingGroup;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
public DirectoryEntry CreateADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
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;
return ent;
}
else
{
DirectoryEntry e = FindGroupEntry(secGroup.Name);
if (e == null)
return null;
var objectid = getSID(e);
secGroup.UID = objectid;
AddMissingMembers(e, secGroup, users);
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 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
}
}