This commit is contained in:
Drechsler, Meik
2025-10-15 14:56:07 +02:00
commit f563d78417
896 changed files with 654481 additions and 0 deletions

161
LIAM.sln Normal file
View File

@@ -0,0 +1,161 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33723.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamTestTeams", "LiamTestTeams\LiamTestTeams.csproj", "{6197C490-07C6-4313-8E0F-AADC27776473}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{451CEE78-7CF7-4691-AAB6-4D636BF22E04}"
ProjectSection(SolutionItems) = preProject
_shared\Matrix42.Common.dll = _shared\Matrix42.Common.dll
SharedAssemblyInfo.cs = SharedAssemblyInfo.cs
_shared\System.Web.Http.dll = _shared\System.Web.Http.dll
_shared\update4u.SPS.DataLayer.dll = _shared\update4u.SPS.DataLayer.dll
_shared\update4u.SPS.DataLayer_alt.dll = _shared\update4u.SPS.DataLayer_alt.dll
_shared\update4u.SPS.Security.dll = _shared\update4u.SPS.Security.dll
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamBaseClasses", "LiamBaseClasses\LiamBaseClasses.csproj", "{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamMsTeams", "LiamMsTeams\LiamMsTeams.csproj", "{DACBD3DC-1866-4B39-964A-D2A8DEA2774C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamHelper", "LiamHelper\LiamHelper.csproj", "{6B0E73A6-F918-42D5-9525-D59D4D16283D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamMsGraph", "LiamMsGraph\LiamMsGraph.csproj", "{452827DB-14FF-469E-AF41-1A24E1875BDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamM42WebApi", "LiamM42WebApi\LiamM42WebApi.csproj", "{007A69B8-3BEA-44F3-BD61-C5354C707F3A}"
ProjectSection(ProjectDependencies) = postProject
{452827DB-14FF-469E-AF41-1A24E1875BDA} = {452827DB-14FF-469E-AF41-1A24E1875BDA}
{DACBD3DC-1866-4B39-964A-D2A8DEA2774C} = {DACBD3DC-1866-4B39-964A-D2A8DEA2774C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamNtfs", "LiamNtfs\LiamNtfs.csproj", "{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamWorkflowActivities", "LiamWorkflowActivities\LiamWorkflowActivities.csproj", "{5840BB2D-88BF-4E1C-8FF6-510305894B42}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamWorkflowActivitiesDesign", "LiamWorkflowActivitiesDesign\LiamWorkflowActivitiesDesign.csproj", "{03D5EBBD-3147-4B30-AE90-6D5C57335E0A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamActiveDirectory", "LIAMActiveDirectory\LiamActiveDirectory.csproj", "{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiamExchange", "LiamExchange\LiamExchange.csproj", "{12586A29-BB1E-49B4-B971-88E520D6A77C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug_and_copy|Any CPU = Debug_and_copy|Any CPU
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6197C490-07C6-4313-8E0F-AADC27776473}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{6197C490-07C6-4313-8E0F-AADC27776473}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{6197C490-07C6-4313-8E0F-AADC27776473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6197C490-07C6-4313-8E0F-AADC27776473}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6197C490-07C6-4313-8E0F-AADC27776473}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6197C490-07C6-4313-8E0F-AADC27776473}.Release|Any CPU.Build.0 = Release|Any CPU
{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}.Release|Any CPU.Build.0 = Release|Any CPU
{DACBD3DC-1866-4B39-964A-D2A8DEA2774C}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{DACBD3DC-1866-4B39-964A-D2A8DEA2774C}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{DACBD3DC-1866-4B39-964A-D2A8DEA2774C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DACBD3DC-1866-4B39-964A-D2A8DEA2774C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DACBD3DC-1866-4B39-964A-D2A8DEA2774C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DACBD3DC-1866-4B39-964A-D2A8DEA2774C}.Release|Any CPU.Build.0 = Release|Any CPU
{6B0E73A6-F918-42D5-9525-D59D4D16283D}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{6B0E73A6-F918-42D5-9525-D59D4D16283D}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{6B0E73A6-F918-42D5-9525-D59D4D16283D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B0E73A6-F918-42D5-9525-D59D4D16283D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B0E73A6-F918-42D5-9525-D59D4D16283D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B0E73A6-F918-42D5-9525-D59D4D16283D}.Release|Any CPU.Build.0 = Release|Any CPU
{452827DB-14FF-469E-AF41-1A24E1875BDA}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{452827DB-14FF-469E-AF41-1A24E1875BDA}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{452827DB-14FF-469E-AF41-1A24E1875BDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{452827DB-14FF-469E-AF41-1A24E1875BDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{452827DB-14FF-469E-AF41-1A24E1875BDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{452827DB-14FF-469E-AF41-1A24E1875BDA}.Release|Any CPU.Build.0 = Release|Any CPU
{007A69B8-3BEA-44F3-BD61-C5354C707F3A}.Debug_and_copy|Any CPU.ActiveCfg = Debug_and_copy|Any CPU
{007A69B8-3BEA-44F3-BD61-C5354C707F3A}.Debug_and_copy|Any CPU.Build.0 = Debug_and_copy|Any CPU
{007A69B8-3BEA-44F3-BD61-C5354C707F3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{007A69B8-3BEA-44F3-BD61-C5354C707F3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{007A69B8-3BEA-44F3-BD61-C5354C707F3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{007A69B8-3BEA-44F3-BD61-C5354C707F3A}.Release|Any CPU.Build.0 = Release|Any CPU
{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}.Release|Any CPU.Build.0 = Release|Any CPU
{5840BB2D-88BF-4E1C-8FF6-510305894B42}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{5840BB2D-88BF-4E1C-8FF6-510305894B42}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{5840BB2D-88BF-4E1C-8FF6-510305894B42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5840BB2D-88BF-4E1C-8FF6-510305894B42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5840BB2D-88BF-4E1C-8FF6-510305894B42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5840BB2D-88BF-4E1C-8FF6-510305894B42}.Release|Any CPU.Build.0 = Release|Any CPU
{03D5EBBD-3147-4B30-AE90-6D5C57335E0A}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{03D5EBBD-3147-4B30-AE90-6D5C57335E0A}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{03D5EBBD-3147-4B30-AE90-6D5C57335E0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{03D5EBBD-3147-4B30-AE90-6D5C57335E0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03D5EBBD-3147-4B30-AE90-6D5C57335E0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03D5EBBD-3147-4B30-AE90-6D5C57335E0A}.Release|Any CPU.Build.0 = Release|Any CPU
{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}.Release|Any CPU.Build.0 = Release|Any CPU
{12586A29-BB1E-49B4-B971-88E520D6A77C}.Debug_and_copy|Any CPU.ActiveCfg = Debug|Any CPU
{12586A29-BB1E-49B4-B971-88E520D6A77C}.Debug_and_copy|Any CPU.Build.0 = Debug|Any CPU
{12586A29-BB1E-49B4-B971-88E520D6A77C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12586A29-BB1E-49B4-B971-88E520D6A77C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12586A29-BB1E-49B4-B971-88E520D6A77C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12586A29-BB1E-49B4-B971-88E520D6A77C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0EC9AD55-4DBA-49C3-BEEF-5EC07E361909}
EndGlobalSection
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 12
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = https://consulting4it.visualstudio.com/
SccLocalPath0 = .
SccProjectUniqueName1 = LiamBaseClasses\\LiamBaseClasses.csproj
SccProjectName1 = LiamBaseClasses
SccLocalPath1 = LiamBaseClasses
SccProjectUniqueName2 = LiamHelper\\LiamHelper.csproj
SccProjectName2 = LiamHelper
SccLocalPath2 = LiamHelper
SccProjectUniqueName3 = LiamM42WebApi\\LiamM42WebApi.csproj
SccProjectName3 = LiamM42WebApi
SccLocalPath3 = LiamM42WebApi
SccProjectUniqueName4 = LiamMsGraph\\LiamMsGraph.csproj
SccProjectName4 = LiamMsGraph
SccLocalPath4 = LiamMsGraph
SccProjectUniqueName5 = LiamMsTeams\\LiamMsTeams.csproj
SccProjectName5 = LiamMsTeams
SccLocalPath5 = LiamMsTeams
SccProjectUniqueName6 = LiamTestTeams\\LiamTestTeams.csproj
SccProjectName6 = LiamTestTeams
SccLocalPath6 = LiamTestTeams
SccProjectUniqueName7 = LiamNtfs\\LiamNtfs.csproj
SccProjectName7 = LiamNtfs
SccLocalPath7 = LiamNtfs
SccProjectUniqueName8 = LiamWorkflowActivities\\LiamWorkflowActivities.csproj
SccProjectName8 = LiamWorkflowActivities
SccLocalPath8 = LiamWorkflowActivities
SccProjectUniqueName9 = LiamWorkflowActivitiesDesign\\LiamWorkflowActivitiesDesign.csproj
SccProjectName9 = LiamWorkflowActivitiesDesign
SccLocalPath9 = LiamWorkflowActivitiesDesign
SccProjectUniqueName10 = LIAMActiveDirectory\\LiamActiveDirectory.csproj
SccProjectName10 = LIAMActiveDirectory
SccLocalPath10 = LIAMActiveDirectory
SccProjectUniqueName11 = LiamExchange\\LiamExchange.csproj
SccProjectName11 = LiamExchange
SccLocalPath11 = LiamExchange
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,371 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using C4IT.Logging;
using C4IT.Matrix42.ServerInfo;
using LiamAD;
using static C4IT.Logging.cLogManager;
using static LiamAD.ADServiceGroupCreator;
using static LiamAD.cActiveDirectoryBase;
namespace C4IT.LIAM
{
public static class LiamInitializer
{
static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
{
return new cLiamProviderAD(LiamConfiguration, ProviderData);
}
}
public class cLiamProviderAD : cLiamProviderBase
{
public static Guid adModuleId = new Guid("e820a625-0653-ee11-b886-00155d300101");
public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
private readonly ADServiceGroupCreator _serviceGroupCreator;
public cLiamProviderAD(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
base(LiamConfiguration, ProviderData)
{
_serviceGroupCreator = new ADServiceGroupCreator(this);
}
public List<Tuple<string, string, string, string>> CreateServiceGroups(
string serviceName,
string description = null,
eLiamAccessRoleScopes gruppenbereich = eLiamAccessRoleScopes.Universal,
ADGroupType gruppentyp = ADGroupType.Distribution,
IEnumerable<string> ownerSidList = null,
IEnumerable<string> memberSidList = null)
{
return _serviceGroupCreator.EnsureServiceGroups(
serviceName,
description,
gruppenbereich,
gruppentyp,
ownerSidList,
memberSidList);
}
public override async Task<bool> LogonAsync()
{
if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
{
LogEntry($"Error: License not valid", LogLevels.Error);
return false;
}
return await LogonAsync(true);
}
public async Task<bool> LogonAsync(bool force = false)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var LI = new cADLogonInfo()
{
Domain = Domain,
User = Credential?.Identification,
UserSecret = Credential?.Secret,
TargetGroupPath = this.GroupPath
};
var RetVal = await activeDirectoryBase.LogonAsync(LI);
return RetVal;
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return false;
}
public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int Depth = -1)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
{
LogEntry($"Error: License not valid", LogLevels.Error);
return new List<cLiamDataAreaBase>();
}
if (!await LogonAsync())
return null;
if (string.IsNullOrEmpty(this.GroupPath))
return null;
// 1. Alle Roh-Resultate einlesen
var rawList = await activeDirectoryBase.RequestSecurityGroupsListAsync(this.GroupFilter);
if (rawList == null)
return null;
// 2. Nur die, die dem RegEx entsprechen und deren Wert extrahieren
var allResults = rawList
.Where(entry =>
string.IsNullOrEmpty(this.GroupRegEx)
|| Regex.Match(entry.Value.DisplayName, this.GroupRegEx).Success)
.Select(entry => (cSecurityGroupResult)entry.Value)
.ToList();
// 3. ManagedBySID-Werte sammeln (ohne Null-/Leereinträge)
var managedBySids = new HashSet<string>(
allResults
.Select(r => r.ManagedBySID)
.Where(m => !string.IsNullOrEmpty(m))
);
// 4. Nur die Gruppen, deren ID nicht in managedBySids enthalten ist
var filteredResults = allResults
.Where(r => !managedBySids.Contains(r.ID))
.ToList();
// 5. In DataArea-Objekte umwandeln
var SecurityGroups = new List<cLiamDataAreaBase>();
foreach (var secGroup in filteredResults)
{
SecurityGroups.Add(new cLiamAdGroupAsDataArea(this, secGroup));
}
return SecurityGroups;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
{
LogEntry($"Error: License not valid", LogLevels.Error);
return new List<cLiamDataAreaBase>();
}
if (!await LogonAsync())
return null;
if (string.IsNullOrEmpty(this.GroupPath))
return null;
var SecurityGroups = new List<cLiamDataAreaBase>();
var SGL = await activeDirectoryBase.RequestSecurityGroupsListAsync(groupFilter);
if (SGL == null)
return null;
foreach (var Entry in SGL)
{
if (!string.IsNullOrEmpty(this.GroupRegEx) && !Regex.Match(Entry.Value.DisplayName, this.GroupRegEx).Success)
continue;
var SecurityGroup = new cLiamAdGroup2(this, (cSecurityGroupResult)Entry.Value);
SecurityGroups.Add(SecurityGroup);
}
return SecurityGroups;
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return null;
}
public int getDepth(string path)
{
return getDepth(this.RootPath, path);
}
public static int getDepth(DirectoryInfo root, DirectoryInfo folder)
{
var rootDepth = root.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length;
var folderDepth = folder.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length;
return folderDepth - rootDepth;
}
public static int getDepth(string root, string folder)
{
return getDepth(new DirectoryInfo(root), new DirectoryInfo(folder));
}
public override string GetLastErrorMessage()
{
throw new NotImplementedException();
}
public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
{
//TODO implement LoadDataArea
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
await Task.Delay(0);
if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(adModuleId))
{
LogEntry($"Error: License not valid", LogLevels.Error);
return null;
}
var res = new cLiamAdGroupAsDataArea(this, new cSecurityGroupResult()
{
Path = UID
});
return res;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
}
public class cLiamAdGroupAsDataArea : cLiamDataAreaBase
{
public new readonly cLiamProviderAD Provider = null;
public readonly string dn = null;
public readonly string scope = null;
public readonly string ManagedBySID;
public override Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
throw new NotImplementedException();
}
public cLiamAdGroupAsDataArea(cLiamProviderAD Provider, cSecurityGroupResult secGroup) : base(Provider)
{
this.UID = secGroup.ID;
this.TechnicalName = secGroup.Path;
this.DisplayName = secGroup.DisplayName;
this.Description = secGroup.Description;
this.Provider = Provider;
this.dn = secGroup.Path;
this.scope = secGroup.Scope.ToString();
this.ManagedBySID = secGroup.ManagedBySID;
}
public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
return await GetMembersAsync(true);
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
private async Task<List<cLiamUserInfo>> GetMembersAsync(bool owners)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var AD = this.Provider?.activeDirectoryBase;
if (AD == null)
{
LogEntry($"Could not get ad class from Provider for folder '{this.TechnicalName}'", LogLevels.Warning);
return null;
}
cADCollectionBase lstMembers;
if (owners && !string.IsNullOrEmpty(OwnerRef))
lstMembers = await AD.GetMembersAsync(OwnerRef);
else if (owners && !string.IsNullOrEmpty(dn))
lstMembers = await AD.GetManagedByMembersAsync(this.dn);
else
lstMembers = null;
if (lstMembers == null)
{
LogEntry($"Could not get owner list for folder '{this.TechnicalName}'", LogLevels.Warning);
return null;
}
var RetVal = new List<cLiamUserInfo>(lstMembers.Count);
LogEntry($"Owners for folder found: {lstMembers.Count}", LogLevels.Debug);
foreach (var MemberEntry in lstMembers.Values)
{
var User = new cLiamUserInfo()
{
DisplayName = MemberEntry.DisplayName,
UserPrincipalName = (MemberEntry as cADUserResult).UserPrincipalName,
SID = MemberEntry.ID
};
RetVal.Add(User);
}
return RetVal;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
}
public class cLiamAdGroup2 : cLiamDataAreaBase
{
public new readonly cLiamProviderAD Provider = null;
public readonly string dn = null;
public readonly string scope = null;
public override Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
throw new NotImplementedException();
}
public cLiamAdGroup2(cLiamProviderAD Provider, cSecurityGroupResult secGroup) : base(Provider)
{
this.DisplayName = secGroup.DisplayName;
this.UID = secGroup.ID;
this.TechnicalName = secGroup.DisplayName;
this.Provider = Provider;
this.dn = secGroup.Path;
this.scope = secGroup.Scope.ToString();
}
}
}

View File

@@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AECA0AD2-8B91-4767-9AFA-E160F6662DBE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LIAMActiveDirectory</RootNamespace>
<AssemblyName>LiamActiveDirectory</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.ValueTuple, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.6.1\lib\net462\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="cActiveDirectoryBase.cs" />
<Compile Include="C4IT.LIAM.AD.cs" />
<Compile Include="cADBase.cs" />
<Compile Include="cADServiceGroupCreator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LiamBaseClasses\LiamBaseClasses.csproj">
<Project>{3531c9e6-cf6e-458e-b604-4a5a8d1c7ab0}</Project>
<Name>LiamBaseClasses</Name>
</ProjectReference>
<ProjectReference Include="..\LiamHelper\LiamHelper.csproj">
<Project>{6b0e73a6-f918-42d5-9525-d59d4d16283d}</Project>
<Name>LiamHelper</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("LIAMActiveDirectory")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LIAMActiveDirectory")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("aeca0ad2-8b91-4767-9afa-e160f6662dbe")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,142 @@
using C4IT.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace LiamAD
{
public class cADBase
{
private cADLogonInfo privLogonInfo = null;
private int scanningDepth;
public PrincipalContext adContext = null;
public Exception LastException { get; private set; } = null;
public string LastErrorMessage { get; private set; } = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetError()
{
LastException = null;
LastErrorMessage = null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetErrorException(string Action, Exception E, LogLevels lev = LogLevels.Error)
{
LastException = E;
LastErrorMessage = Action + ": " + E.Message;
cLogManager.LogEntry(Action, lev);
}
[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags);
[DllImport("mpr.dll")]
private static extern int WNetCancelConnection2(string name, int flags,
bool force);
}
public class cADLogonInfo
{
public string Domain;
public string User;
public string UserSecret;
public string TargetGroupPath;
}
[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
public ResourceScope Scope;
public ResourceType ResourceType;
public ResourceDisplaytype DisplayType;
public int Usage;
public string LocalName;
public string RemoteName;
public string Comment;
public string Provider;
}
public enum ResourceScope : int
{
Connected = 1,
GlobalNetwork,
Remembered,
Recent,
Context
};
public enum ResourceType : int
{
Any = 0,
Disk = 1,
Print = 2,
Reserved = 8,
}
public enum ResourceDisplaytype : int
{
Generic = 0x0,
Domain = 0x01,
Server = 0x02,
Share = 0x03,
File = 0x04,
Group = 0x05,
Network = 0x06,
Root = 0x07,
Shareadmin = 0x08,
Directory = 0x09,
Tree = 0x0a,
Ndscontainer = 0x0b
}
public enum NetError : uint
{
NERR_Success = 0,
NERR_BASE = 2100,
NERR_UnknownDevDir = (NERR_BASE + 16),
NERR_DuplicateShare = (NERR_BASE + 18),
NERR_BufTooSmall = (NERR_BASE + 23),
}
public enum SHARE_TYPE : uint
{
STYPE_DISKTREE = 0,
STYPE_PRINTQ = 1,
STYPE_DEVICE = 2,
STYPE_IPC = 3,
STYPE_SPECIAL = 0x80000000,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHARE_INFO_1
{
public string shi1_netname;
public uint shi1_type;
public string shi1_remark;
public SHARE_INFO_1(string sharename, uint sharetype, string remark)
{
this.shi1_netname = sharename;
this.shi1_type = sharetype;
this.shi1_remark = remark;
}
public override string ToString()
{
return shi1_netname;
}
}
}

View File

@@ -0,0 +1,288 @@
using System;
using System.DirectoryServices;
using System.Linq;
using System.Threading;
using C4IT.Logging;
using C4IT.LIAM;
using LiamAD;
using System.Collections.Generic;
using System.Security.Principal;
using System.Text;
namespace LiamAD
{
/// <summary>
/// Helfer für cLiamProviderAD: Erstellt AD Member- und Owner-Gruppen für Services
/// nach konfigurierter Namenskonvention und setzt ManagedBy.
/// </summary>
public class ADServiceGroupCreator
{
private readonly cLiamProviderAD _provider;
private readonly cActiveDirectoryBase _adBase;
private readonly string _ldapRoot;
private readonly string _user;
private readonly string _password;
public enum ADGroupType
{
Security, // Sicherheit
Distribution // Verteiler
}
public ADServiceGroupCreator(cLiamProviderAD provider)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
_adBase = provider.activeDirectoryBase;
_ldapRoot = $"LDAP://{provider.Domain}/{provider.GroupPath}";
_user = provider.Credential.Identification;
_password = new System.Net.NetworkCredential(_user, provider.Credential.Secret).Password;
}
/// <summary>
/// Erstellt oder findet beide AD-Gruppen (Member & Owner) für einen Service.
/// Neu mit: gruppenbereich (Scope) und gruppentyp (für Member-Gruppe).
/// Owner-Gruppe ist immer Security.
/// </summary>
public List<Tuple<string, string, string, string>> EnsureServiceGroups(
string serviceName,
string description = null,
eLiamAccessRoleScopes gruppenbereich = eLiamAccessRoleScopes.Universal,
ADGroupType gruppentyp = ADGroupType.Distribution,
IEnumerable<string> ownerSidList = null,
IEnumerable<string> memberSidList = null)
{
const int MaxLoop = 50;
var result = new List<Tuple<string, string, string, string>>();
// Konventionen für Member und Owner
var ownerConv = _provider.NamingConventions
.FirstOrDefault(nc => nc.AccessRole == eLiamAccessRoles.ADOwner);
var memberConv = _provider.NamingConventions
.FirstOrDefault(nc => nc.AccessRole == eLiamAccessRoles.ADMember);
if (ownerConv == null || memberConv == null)
throw new InvalidOperationException("Namenskonvention für ADMember oder ADOwner fehlt.");
// Tags
_provider.CustomTags.TryGetValue("ADGroupPrefix", out var prefix);
_provider.CustomTags.TryGetValue("ADOwner", out var ownerPostfix);
_provider.CustomTags.TryGetValue("ADMember", out var memberPostfix);
// 1) Owner-Gruppe (immer Security)
string ownerName = null;
for (int loop = 0; loop <= MaxLoop; loop++)
{
string loopPart = loop > 0 ? "_" + loop : string.Empty;
ownerName = ownerConv.NamingTemplate
.Replace("{{ADGroupPrefix}}", prefix ?? string.Empty)
.Replace("{{NAME}}", serviceName)
.Replace("{{_LOOP}}", loopPart)
.Replace("{{GROUPTYPEPOSTFIX}}", ownerPostfix);
if (!GroupExists(ownerName)) break;
if (loop == MaxLoop) throw new InvalidOperationException($"Kein eindeutiger Owner-Name für '{serviceName}' nach {MaxLoop} Versuchen.");
}
EnsureGroup(ownerName, ownerConv, description, managedByDn: null, gruppenbereich, ADGroupType.Security);
AddMembersBySid(ownerName, ownerSidList); // NEU: SIDs als Owner hinzufügen
var ownerDn = GetDistinguishedName(ownerName);
var ownerSid = GetSid(ownerName);
result.Add(Tuple.Create(eLiamAccessRoles.ADOwner.ToString(), ownerSid, ownerName, ownerDn));
// 2) Member-Gruppe (Gruppentyp nach Parameter)
string memberName = null;
for (int loop = 0; loop <= MaxLoop; loop++)
{
string loopPart = loop > 0 ? "_" + loop : string.Empty;
memberName = memberConv.NamingTemplate
.Replace("{{ADGroupPrefix}}", prefix ?? string.Empty)
.Replace("{{NAME}}", serviceName)
.Replace("{{_LOOP}}", loopPart)
.Replace("{{GROUPTYPEPOSTFIX}}", memberPostfix);
if (!GroupExists(memberName)) break;
if (loop == MaxLoop) throw new InvalidOperationException($"Kein eindeutiger Member-Name für '{serviceName}' nach {MaxLoop} Versuchen.");
}
EnsureGroup(memberName, memberConv, description, managedByDn: ownerDn, gruppenbereich, gruppentyp);
AddMembersBySid(memberName, memberSidList); // NEU: SIDs als Member hinzufügen
var memberDn = GetDistinguishedName(memberName);
var memberSid = GetSid(memberName);
result.Add(Tuple.Create(eLiamAccessRoles.ADMember.ToString(), memberSid, memberName, memberDn));
return result;
}
/// <summary>
/// Fügt einer bestehenden Gruppe per SID die entsprechenden AD-Objekte hinzu.
/// </summary>
private void AddMembersBySid(string groupName, IEnumerable<string> sidList)
{
if (sidList == null) return;
// Basis für die Suche: komplette Domäne, nicht nur der OU-Pfad
string domainRoot = $"LDAP://{_provider.Domain}";
using (var root = new DirectoryEntry(domainRoot, _user, _password, AuthenticationTypes.Secure))
using (var grpSearch = new DirectorySearcher(root))
{
grpSearch.Filter = $"(&(objectCategory=group)(sAMAccountName={groupName}))";
var grpRes = grpSearch.FindOne();
if (grpRes == null) return;
var grpEntry = grpRes.GetDirectoryEntry();
foreach (var sidStr in sidList)
{
// Leere oder null überspringen
if (string.IsNullOrWhiteSpace(sidStr))
continue;
SecurityIdentifier sid;
try
{
sid = new SecurityIdentifier(sidStr);
}
catch (Exception)
{
// Ungültige SID-String-Darstellung überspringen
continue;
}
// In LDAP-Filter-Notation umwandeln
var bytes = new byte[sid.BinaryLength];
sid.GetBinaryForm(bytes, 0);
var sb = new StringBuilder();
foreach (var b in bytes)
sb.AppendFormat("\\{0:X2}", b);
string octetSid = sb.ToString();
// Suche nach dem Objekt in der Domäne
using (var usrSearch = new DirectorySearcher(root))
{
usrSearch.Filter = $"(objectSid={octetSid})";
var usrRes = usrSearch.FindOne();
if (usrRes == null)
continue;
var userDn = usrRes.Properties["distinguishedName"][0].ToString();
// Doppelteinträge vermeiden
if (!grpEntry.Properties["member"].Contains(userDn))
grpEntry.Properties["member"].Add(userDn);
}
}
grpEntry.CommitChanges();
}
}
/// <summary>
/// Wandelt eine SID (String-Form) in das für LDAP nötige Oktet-String-Format um.
/// </summary>
private string SidStringToLdapFilter(string sidString)
{
var sid = new SecurityIdentifier(sidString);
var bytes = new byte[sid.BinaryLength];
sid.GetBinaryForm(bytes, 0);
var sb = new StringBuilder();
foreach (var b in bytes)
sb.AppendFormat("\\{0:X2}", b);
return sb.ToString();
}
private string GetSid(string name)
{
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure))
using (var ds = new DirectorySearcher(root))
{
ds.Filter = $"(&(objectCategory=group)(sAMAccountName={name}))";
var r = ds.FindOne();
if (r == null) return null;
var de = r.GetDirectoryEntry();
var sidBytes = (byte[])de.Properties["objectSid"][0];
return new SecurityIdentifier(sidBytes, 0).Value;
}
}
private string FormatName(cLiamNamingConvention conv, string serviceName, System.Collections.Generic.IDictionary<string, string> tags)
{
string tmpl = conv.NamingTemplate.Replace("{{NAME}}", serviceName);
foreach (var kv in tags)
tmpl = tmpl.Replace("{{" + kv.Key + "}}", kv.Value);
return tmpl;
}
/// <summary>
/// Stellt sicher, dass die Gruppe existiert neu mit Scope & Type.
/// </summary>
private void EnsureGroup(
string groupName,
cLiamNamingConvention conv,
string description,
string managedByDn,
eLiamAccessRoleScopes groupScope,
ADGroupType groupType)
{
if (!GroupExists(groupName))
{
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure))
{
var grp = root.Children.Add("CN=" + groupName, "group");
grp.Properties["sAMAccountName"].Value = groupName;
grp.Properties["displayName"].Value = groupName;
// Hier: Security-Bit (0x80000000) nur, wenn Security, sonst 0
int typeBit = (groupType == ADGroupType.Security)
? unchecked((int)0x80000000)
: 0;
// Scope-Bit aus Param
grp.Properties["groupType"].Value = unchecked(typeBit | GetScopeBit(groupScope));
if (!string.IsNullOrEmpty(description))
grp.Properties["description"].Value = description;
if (managedByDn != null)
grp.Properties["managedBy"].Value = managedByDn;
grp.CommitChanges();
}
WaitReplication(groupName, TimeSpan.FromMinutes(2));
}
}
private bool GroupExists(string name)
{
return _adBase.directoryEntry.Children.Cast<DirectoryEntry>()
.Any(c => string.Equals(
c.Properties["sAMAccountName"]?.Value?.ToString(), name, StringComparison.OrdinalIgnoreCase));
}
private void WaitReplication(string groupName, TimeSpan timeout)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
while (sw.Elapsed < timeout)
{
if (GroupExists(groupName))
return;
Thread.Sleep(2000);
}
}
private string GetDistinguishedName(string name)
{
using (var root = new DirectoryEntry(_ldapRoot, _user, _password, AuthenticationTypes.Secure))
using (var ds = new DirectorySearcher(root))
{
ds.Filter = "(&(objectClass=group)(sAMAccountName=" + name + "))";
var res = ds.FindOne();
return res?.Properties["distinguishedName"]?[0]?.ToString();
}
}
private int GetScopeBit(eLiamAccessRoleScopes scope)
{
switch (scope)
{
case eLiamAccessRoleScopes.Universal:
return 0x8;
case eLiamAccessRoleScopes.Global:
return 0x2;
case eLiamAccessRoleScopes.DomainLocal:
return 0x4;
default:
return 0x8;
}
}
}
}

View File

@@ -0,0 +1,516 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
namespace LiamAD
{
public class cActiveDirectoryBase
{
private cADLogonInfo privLogonInfo = null;
public PrincipalContext adContext = null;
public DirectoryEntry directoryEntry = null;
public Exception LastException { get; private set; } = null;
public string LastErrorMessage { get; private set; } = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetError()
{
LastException = null;
LastErrorMessage = null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetErrorException(string Action, Exception E, LogLevels lev = LogLevels.Error)
{
LastException = E;
LastErrorMessage = Action + ": " + E.Message;
cLogManager.LogEntry(Action, lev);
}
private async Task<bool> privLogonAsync(cADLogonInfo LogonInfo)
{
try
{
//TODO: remove dummy delay?
await Task.Delay(0);
ResetError();
adContext = new PrincipalContext(ContextType.Domain, LogonInfo.Domain, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password);
var ldapPath = $"LDAP://{LogonInfo.Domain}/{LogonInfo.TargetGroupPath}";
directoryEntry = new DirectoryEntry
{
Path = ldapPath,
Username = LogonInfo.User,
Password = new NetworkCredential(LogonInfo.User, LogonInfo.UserSecret).Password,
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
};
return adContext != null;
}
catch (Exception E)
{
SetErrorException("exception error while ad login", E, LogLevels.Debug);
cLogManager.LogException(E, LogLevels.Debug);
}
return false;
}
private async Task<bool> privRelogon()
{
if (privLogonInfo == null)
return false;
var RetVal = await privLogonAsync(privLogonInfo);
return RetVal;
}
public async Task<bool> LogonAsync(cADLogonInfo LogonInfo)
{
var RetVal = await privLogonAsync(LogonInfo);
if (RetVal == true)
privLogonInfo = LogonInfo;
return RetVal;
}
internal AuthorizationRuleCollection GetAccessControlList(string path)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
DirectoryInfo dADir = new DirectoryInfo(path);
var dAACL = dADir.GetAccessControl();
return dAACL.GetAccessRules(true, false, typeof(System.Security.Principal.SecurityIdentifier));
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
internal string resolveSid(string sid)
{
try
{
return new System.Security.Principal.SecurityIdentifier(sid).Translate(typeof(System.Security.Principal.NTAccount)).ToString();
}
catch (Exception E)
{
LogException(E);
return null;
}
}
internal async Task<cADCollectionBase> RequestSecurityGroupsListAsync(string groupFilter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
await Task.Delay(0);
var Result = privRequestSecurityGroupsListAsync(groupFilter);
if (Result != null)
{
var RetVal = new cADCollectionBase(Result.Count);
foreach (var Entry in Result)
{
var res = new cSecurityGroupResult(Entry);
RetVal.Add(res);
}
return RetVal;
}
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
return null;
}
private List<cSecurityGroupResult> privRequestSecurityGroupsListAsync(string groupFilter = null, string rawLDAPFilter = null)
{
ResetError();
List<cSecurityGroupResult> securityGroups = new List<cSecurityGroupResult>();
if (String.IsNullOrEmpty(privLogonInfo.TargetGroupPath) ||
(string.IsNullOrEmpty(groupFilter) && string.IsNullOrEmpty(rawLDAPFilter)))
{
return new List<cSecurityGroupResult>();
}
try
{
var ctx = adContext;
var entry = directoryEntry;
using (DirectorySearcher dSearch = new DirectorySearcher(entry))
{
dSearch.Filter = string.IsNullOrEmpty(rawLDAPFilter) ? "(&(" + groupFilter + ")(objectClass=group))" : rawLDAPFilter;
dSearch.PageSize = 100000;
SearchResultCollection sr = dSearch.FindAll();
if (sr.Count > 0)
{
foreach (SearchResult k in sr)
{
var sid = new SecurityIdentifier(k.Properties["objectSid"][0] as byte[], 0).Value;
var dn = k.Properties["distinguishedname"][0].ToString();
// Initialisieren Sie die managedBy-SID als null
string managedBySid = null;
// Prüfen, ob das managedBy-Attribut existiert und nicht null ist
if (k.Properties.Contains("managedBy") && k.Properties["managedBy"].Count > 0)
{
// managedBy-DN erhalten
string managedByDn = k.Properties["managedBy"][0].ToString();
// Erstellen eines DirectoryEntry-Objekts für den managedBy-DN
using (DirectoryEntry managedByEntry = new DirectoryEntry($"LDAP://{managedByDn}"))
{
if (managedByEntry.Properties.Contains("objectSid") && managedByEntry.Properties["objectSid"].Count > 0)
{
// SID des managedBy-Objekts erhalten
managedBySid = new SecurityIdentifier(managedByEntry.Properties["objectSid"][0] as byte[], 0).Value;
}
}
}
// Neues SecurityGroup-Objekt erstellen
cSecurityGroupResult group = new cSecurityGroupResult()
{
ID = sid,
Path = dn,
DisplayName = k.Properties["Name"][0].ToString(),
Description = k.Properties.Contains("Description") ? k.Properties["Description"][0].ToString() : string.Empty,
Scope = (GroupScope)GroupPrincipal.FindByIdentity(ctx, IdentityType.Sid, sid).GroupScope,
ManagedBySID = managedBySid
};
securityGroups.Add(group);
}
}
}
}
catch (Exception e)
{
cLogManager.LogException(e);
return new List<cSecurityGroupResult>(securityGroups);
}
return securityGroups;
}
public class cADCollectionBase : SortedList<string, cADResultBase>
{
public cADCollectionBase() { }
public cADCollectionBase(int n) : base(n) { }
public void Add(cADResultBase adr)
{
if (!this.ContainsKey(adr.ID))
this.Add(adr.ID, adr);
}
}
public class cSecurityGroupResult : cADResultBase
{
public cSecurityGroupResult() { }
public cSecurityGroupResult(cADResultBase b) : base(b)
{
this.ManagedBySID = (b as cSecurityGroupResult)?.ManagedBySID;
}
public GroupScope Scope { get; internal set; }
public string ManagedBySID { get; internal set; }
}
public class cADUserResult : cADResultBase
{
public string GivenName { get; internal set; }
public string SurName { get; internal set; }
public string UserPrincipalName { get; internal set; }
public string Email { get; internal set; }
public cADUserResult() { }
public cADUserResult(cADResultBase b) : base(b)
{
}
public cADUserResult(Principal Result) : base(Result)
{
UserPrincipalName = Result.UserPrincipalName;
}
public GroupScope Scope { get; internal set; }
}
public class cADResultBase
{
public string ID { get; set; } = null;
public string DisplayName { get; set; } = null;
public string Path { get; set; } = null;
public DateTime CreatedDate { get; set; } = DateTime.MinValue;
public string Description { get; set; } = null;
public cADResultBase()
{ }
public cADResultBase(cADResultBase Result)
{
if (Result == null)
return;
ID = Result.ID;
DisplayName = Result.DisplayName;
Description = Result.Description;
Path = Result.Path;
}
public cADResultBase(Principal Result)
{
if (Result == null)
return;
ID = Result.Sid.ToString();
DisplayName = Result.DisplayName;
Path = Result.DistinguishedName;
}
}
/// <summary>
/// Asynchrones Abrufen des managedBy-Attributs einer AD-Gruppe anhand ihrer SID.
/// Gibt den Distinguished Name (DN) des Managers zurück.
/// </summary>
/// <param name="groupSid">Die SID der AD-Gruppe.</param>
/// <returns>Der DN des Managers oder null, falls nicht gefunden.</returns>
public async Task<string> GetManagedByDnAsync(string dn)
{
try
{
// Simuliert einen asynchronen Aufruf
await Task.Yield();
using (var group = GroupPrincipal.FindByIdentity(adContext, IdentityType.DistinguishedName, dn))
{
if (group == null)
{
cLogManager.LogEntry($"Gruppe mit dn {dn} nicht gefunden.");
return null;
}
// Zugriff auf das underlying DirectoryEntry, um das managedBy-Attribut zu lesen
var directoryEntry = group.GetUnderlyingObject() as DirectoryEntry;
if (directoryEntry != null && directoryEntry.Properties.Contains("managedBy"))
{
var managedByValue = directoryEntry.Properties["managedBy"].Value as string;
if (!string.IsNullOrEmpty(managedByValue))
{
return managedByValue;
}
else
{
cLogManager.LogEntry($"managedBy-Attribut für Gruppe mit dn {dn} ist leer.");
}
}
else
{
cLogManager.LogEntry($"Gruppe mit dn {dn} hat kein managedBy-Attribut.");
}
}
}
catch (Exception ex)
{
cLogManager.LogException(ex);
}
return null;
}
/// <summary>
/// Asynchrones Abrufen der Mitglieder des Managers einer AD-Gruppe anhand des groupSid.
/// Verwendet den DN des Managers.
/// </summary>
/// <param name="groupSid">Die SID der AD-Gruppe.</param>
/// <returns>Eine cADCollectionBase mit den Mitgliedern oder null.</returns>
public async Task<cADCollectionBase> GetManagedByMembersAsync(string groupSid)
{
try
{
var managedByDn = await GetManagedByDnAsync(groupSid);
if (!string.IsNullOrEmpty(managedByDn))
{
return await GetMembersByDnAsync(managedByDn);
}
else
{
cLogManager.LogEntry($"Keine gültige managedBy DN für Gruppe mit SID {groupSid} gefunden.");
}
}
catch (Exception ex)
{
cLogManager.LogException(ex);
}
return null;
}
/// <summary>
/// Asynchrones Abrufen der Mitglieder einer AD-Gruppe anhand des Distinguished Name (DN).
/// </summary>
/// <param name="dn">Der Distinguished Name der AD-Gruppe.</param>
/// <returns>Eine cADCollectionBase mit den Mitgliedern oder null.</returns>
public async Task<cADCollectionBase> GetMembersByDnAsync(string dn)
{
try
{
// Simuliert einen asynchronen Aufruf
await Task.Yield();
var result = privGetMembersByDnAsync(dn).ToList();
if (result != null && result.Any())
{
var retVal = new cADCollectionBase(result.Count);
foreach (var entry in result)
{
var res = new cADUserResult(entry);
if (!string.IsNullOrEmpty(res.Path))
retVal.Add(res);
}
return retVal;
}
else
{
cLogManager.LogEntry($"Keine Mitglieder für Gruppe mit DN {dn} gefunden.");
}
}
catch (Exception e)
{
cLogManager.LogException(e);
}
return null;
}
/// <summary>
/// Interne Methode zum Abrufen der Mitglieder einer AD-Gruppe anhand des Distinguished Name (DN).
/// </summary>
/// <param name="dn">Der Distinguished Name der AD-Gruppe.</param>
/// <returns>Eine PrincipalSearchResult mit den Mitgliedern oder null.</returns>
private PrincipalSearchResult<Principal> privGetMembersByDnAsync(string dn)
{
try
{
using (var group = GroupPrincipal.FindByIdentity(adContext, IdentityType.DistinguishedName, dn))
{
if (group == null)
{
cLogManager.LogEntry($"Gruppe mit DN {dn} nicht gefunden.");
return null;
}
else
{
return group.GetMembers(true);
}
}
}
catch (Exception e)
{
cLogManager.LogException(e);
}
return null;
}
/// <summary>
/// Asynchrones Abrufen der Mitglieder einer AD-Gruppe anhand ihrer SID.
/// Diese Methode bleibt unverändert und kann weiterhin verwendet werden.
/// </summary>
/// <param name="sid">Die SID der AD-Gruppe.</param>
/// <returns>Eine cADCollectionBase mit den Mitgliedern oder null.</returns>
internal async Task<cADCollectionBase> GetMembersAsync(string sid)
{
try
{
// Simuliert einen asynchronen Aufruf
await Task.Yield();
var result = privGetMembersAsync(sid).ToList();
if (result != null && result.Any())
{
var retVal = new cADCollectionBase(result.Count);
foreach (var entry in result)
{
var res = new cADUserResult(entry);
if (!string.IsNullOrEmpty(res.Path))
retVal.Add(res);
}
return retVal;
}
else
{
cLogManager.LogEntry($"Keine Mitglieder für Gruppe mit SID {sid} gefunden.");
}
}
catch (Exception e)
{
cLogManager.LogException(e);
}
return null;
}
/// <summary>
/// Interne Methode zum Abrufen der Mitglieder einer AD-Gruppe anhand ihrer SID.
/// Diese Methode bleibt unverändert und kann weiterhin verwendet werden.
/// </summary>
/// <param name="sid">Die SID der AD-Gruppe.</param>
/// <returns>Eine PrincipalSearchResult mit den Mitgliedern oder null.</returns>
private PrincipalSearchResult<Principal> privGetMembersAsync(string sid)
{
try
{
using (var group = GroupPrincipal.FindByIdentity(adContext, IdentityType.Sid, sid))
{
if (group == null)
{
cLogManager.LogEntry($"Gruppe mit SID {sid} nicht gefunden.");
return null;
}
else
{
return group.GetMembers(true);
}
}
}
catch (Exception e)
{
cLogManager.LogException(e);
}
return null;
}
}
}

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]

View File

@@ -0,0 +1,14 @@
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\LiamActiveDirectory.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\LiamActiveDirectory.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\LiamBaseClasses.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\LiamHelper.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\Newtonsoft.Json.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\LiamBaseClasses.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\LiamHelper.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Debug\LiamActiveDirectory.csproj.AssemblyReference.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Debug\LiamActiveDirectory.csproj.CoreCompileInputs.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Debug\LiamActi.8091FDFC.Up2Date
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Debug\LiamActiveDirectory.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Debug\LiamActiveDirectory.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\System.ValueTuple.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Debug\System.ValueTuple.xml

View File

@@ -0,0 +1 @@
88ca960765b758a5c4edcddedf6dc811f2a49d840cd7ade015d7644911a99c77

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]

View File

@@ -0,0 +1,14 @@
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\LIAMActiveDirectory.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\LIAMActiveDirectory.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\LiamBaseClasses.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\LiamHelper.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\Newtonsoft.Json.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\LiamBaseClasses.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\LiamHelper.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Release\LIAMActiveDirectory.csproj.AssemblyReference.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Release\LIAMActiveDirectory.csproj.CoreCompileInputs.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Release\LIAMActiveDirectory.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Release\LIAMActiveDirectory.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\obj\Release\LiamActi.8091FDFC.Up2Date
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\System.ValueTuple.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LIAMActiveDirectory\bin\Release\System.ValueTuple.xml

View File

@@ -0,0 +1 @@
ab9bb136583040c5ab0b8fc2b80edba3f154caa1532f30973b39973f0def47e6

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.ValueTuple" version="4.6.1" targetFramework="net462" />
</packages>

View File

@@ -0,0 +1,399 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
namespace C4IT.LIAM
{
public enum eLiamAccessRoles
{
Owner = 1, Write = 2, Read = 3, Traverse = 4,
ADOwner = 110, // "AD Owner Group"
ADMember = 100, // "AD Member Group"
ExchangeMLMember = 200, // "Mailing List Member"
ExchangeMLOwner = 210, // "Mailing List Owner"
ExchangeSMBFullAccess = 250, // "Shared Mailbox Full Access"
ExchangeSMBSendAs = 260, // "Shared Mailbox Send as"
ExchangeSMBOwner = 270 // "Shared Mailbox Owner"
};
public enum eLiamAccessRoleScopes { Unknown = 0, Universal = 1, Global = 2, DomainLocal = 3 };
public enum eLiamProviderTypes { Unknown = 0, Ntfs = 1, Sharepoint = 2, Matrix42 = 3, MsTeams = 4, ActiveDirectory = 5, Exchange = 6 };
public enum eLiamDataAreaTypes
{
Unknown = 0,
NtfsShare = 101,
NtfsFolder = 102,
MsTeamsTeam = 401,
MsTeamsChannel = 402,
MsTeamsFolder = 403,
ActiveDirectoryGroup = 501,
ExchangeSharedMailbox = 601,
ExchangeDistributionGroup = 602
};
public enum eLiamGroupStrategies { None = 0, Ntfs_AGP = 0, Ntfs_AGDLP = 1 };
public class cLiamConfiguration
{
}
public class cLiamProviderData : ICloneable
{
public eLiamProviderTypes ProviderType { get; set; } = eLiamProviderTypes.Unknown;
public string Domain { get; set; } = "";
public string RootPath { get; set; } = "";
public cLiamCredential Credential { get; set; } = null;
public int MaxDepth { get; set; } = 1;
public eLiamGroupStrategies GroupStrategy { get; set; } = eLiamGroupStrategies.None;
public string DataAreaFilter { get; set; } = "";
public string DataAreaRegEx { get; set; } = "";
public string GroupFilter { get; set; } = "";
public string GroupRegEx { get; set; } = "";
public string GroupPath { get; set; } = "";
public string OwnerGroupGlobal { get; set; } = "";
public string OwnerGroupLocal { get; set; } = "";
public string WriteGroupGlobal { get; set; } = "";
public string WriteGroupLocal { get; set; } = "";
public string ReadGroupGlobal { get; set; } = "";
public string ReadGroupLocal { get; set; } = "";
public string TraverseGroup { get; set; } = "";
public List<cLiamNamingConvention> NamingConventions { get; set; } = new List<cLiamNamingConvention>();
public Dictionary<string, string> CustomTags { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> AdditionalConfiguration { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public static void Copy(cLiamProviderData From, cLiamProviderData To)
{
To.ProviderType = From.ProviderType;
To.Domain = From.Domain;
To.RootPath = From.RootPath;
To.Credential = From.Credential;
To.MaxDepth = From.MaxDepth;
To.GroupStrategy = From.GroupStrategy;
To.DataAreaFilter = From.DataAreaFilter;
To.DataAreaRegEx = From.DataAreaRegEx;
To.GroupFilter = From.GroupFilter;
To.GroupRegEx = From.GroupRegEx;
To.GroupPath = From.GroupPath;
To.CustomTags = From.CustomTags;
To.AdditionalConfiguration = From.AdditionalConfiguration;
To.CustomTags = From.CustomTags;
To.NamingConventions = From.NamingConventions;
}
public cLiamProviderData()
{
}
public cLiamProviderData(cLiamProviderData PD)
{
Copy(PD, this);
}
public object Clone()
{
return new cLiamProviderData(this);
}
public void ReplaceCustomTags()
{
foreach (var customTag in this.CustomTags)
{
var Key = customTag.Key;
foreach (var namingConvention in NamingConventions)
{
if (customTag.Key == "Filesystem_GroupDomainLocalTag" && namingConvention.Scope == eLiamAccessRoleScopes.DomainLocal || customTag.Key == "Filesystem_GroupGlobalTag" && namingConvention.Scope == eLiamAccessRoleScopes.Global)
Key = "SCOPETAG";
else
Key = customTag.Key;
namingConvention.DescriptionTemplate = namingConvention.DescriptionTemplate.Replace($"{{{{{Key}}}}}", customTag.Value);
namingConvention.NamingTemplate = namingConvention.NamingTemplate.Replace($"{{{{{Key}}}}}", customTag.Value);
namingConvention.Wildcard = namingConvention.Wildcard.Replace($"{{{{{Key}}}}}", customTag.Value);
}
GroupFilter = GroupFilter.Replace($"{{{{{Key}}}}}", customTag.Value);
}
}
}
public abstract class cLiamProviderBase : cLiamProviderData
{
public cLiamConfiguration LiamConfiguration { get; private set; }
public cLiamProviderBase(cLiamConfiguration Configuration, cLiamProviderData ProviderData) :
base(ProviderData)
{
LiamConfiguration = Configuration;
}
public abstract Task<List<cLiamDataAreaBase>> getDataAreasAsync(int MaxDepth = -1);
public abstract Task<bool> LogonAsync();
public abstract string GetLastErrorMessage();
public abstract Task<cLiamDataAreaBase> LoadDataArea(string UID);
public static cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var MyPath = Assembly.GetExecutingAssembly()?.Location;
if (MyPath == null)
return null;
MyPath = Path.GetDirectoryName(MyPath);
var DllPath = Path.Combine(MyPath, $"Liam{ProviderData.ProviderType}.dll");
if (!File.Exists(DllPath))
{
LogEntry($"Couldn not found Data Provider library '{DllPath}'", LogLevels.Error);
return null;
}
var assLocal = Assembly.LoadFrom(DllPath);
if (assLocal == null)
{
LogEntry($"Could not load Data Provider library '{DllPath}'", LogLevels.Error);
return null;
}
var TP = assLocal.GetTypes();
var type = assLocal.GetType("C4IT.LIAM.LiamInitializer");
if (type == null)
{
LogEntry($"Could not found class 'C4IT.LIAM.LiamInitializer' in library '{DllPath}'", LogLevels.Error);
return null;
}
var MI = type.GetMethod("CreateInstance");
if (MI == null)
{
LogEntry($"Could not found method 'CreateInstance' in class 'C4IT.LIAM.LiamInitializer' in library '{DllPath}'", LogLevels.Error);
return null;
}
var objDll = MI.Invoke(null, new object[] { LiamConfiguration, ProviderData });
var RetVal = objDll as cLiamProviderBase;
ProviderData.ReplaceCustomTags();
return RetVal;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public abstract Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter);
}
public class cLiamCredential
{
public string Domain { get; set; } = "";
public string Identification { get; set; } = "";
public string Secret { get; set; } = "";
}
public class cLiamNamingConvention
{
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string NamingTemplate { get; set; } = "";
public string DescriptionTemplate { get; set; } = "";
public string Wildcard { get; set; } = "";
public eLiamAccessRoles AccessRole { get; set; } = eLiamAccessRoles.Read;
public eLiamAccessRoleScopes Scope { get; set; } = eLiamAccessRoleScopes.Unknown;
public eLiamProviderTypes? ProviderType { get; set; } = null;
}
public class cLiamDataAreaInfo
{
public string DisplayName { get; protected set; } = null;
public string Description { get; protected set; } = null;
public string OwnerRef { get; set; } = null;
public string UID { get; protected set; } = null;
public string CreatedDate { get; protected set; } = null;
public string TechnicalName { get; protected set; } = "";
public int Level { get; set; } = -1;
public string ParentUID { get; protected set; } = null;
public eLiamDataAreaTypes DataType { get; protected set; } = eLiamDataAreaTypes.Unknown;
public bool SupportsPermissions { get; protected set; } = false;
public bool SupportsOwners { get; protected set; } = false;
}
public abstract class cLiamDataAreaBase : cLiamDataAreaInfo
{
public readonly cLiamProviderBase Provider = null;
public cLiamDataAreaBase(cLiamProviderBase Provider)
{
this.Provider = Provider;
}
public abstract Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1);
public static async Task<List<cLiamDataAreaBase>> getChildrenFromListAsync(List<cLiamDataAreaBase> Items, int Depth = -1)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var RetVal = new List<cLiamDataAreaBase>();
try
{
if (Items == null)
return RetVal;
if (Depth <= 0)
return RetVal;
foreach (var Entry in Items)
{
var Childs = await Entry.getChildrenAsync(Depth + 1);
if (Childs != null)
RetVal.AddRange(Childs);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return RetVal;
}
public virtual async Task<List<cLiamUserInfo>> GetOwnersAsync()
{
await Task.Delay(0);
return null;
}
public virtual async Task<List<cLiamPermissionInfo>> GetPermissionsAsync(bool force)
{
await Task.Delay(0);
return null;
}
public virtual async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
await Task.Delay(0);
return null;
}
public virtual async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
await Task.Delay(0);
return false;
}
}
public class cLiamUserInfo
{
public string DisplayName { get; set; } = null;
public string GivenName { get; set; } = null;
public string SurName { get; set; } = null;
public string UserPrincipalName { get; set; } = null;
public string EMail { get; set; } = null;
public string SID { get; set; } = null;
}
public class cLiamPermissionResult
{
public bool Valid { get; set; } = false;
public string UserReference { get; set; } = null;
}
public class cLiamPermissionInfo
{
public cLiamUserInfo User;
public eLiamAccessRoles AccessRole = eLiamAccessRoles.Read;
public bool OnlyEffective = false;
}
public class DataAreaEntryBase
{
public string DisplayName { get; set; }
public string TechnicalName { get; set; }
public string UID { get; set; }
public string TargetType { get; set; }
}
public class SecurityGroupEntry : DataAreaEntryBase
{
public string Scope { get; set; }
}
public class DataAreaEntry : DataAreaEntryBase
{
public string Parent { get; set; }
public string ParentUID { get; set; }
public string Owner { get; set; }
public string Write { get; set; }
public string Read { get; set; }
public string Traverse { get; set; }
public string CreatedDate { get; set; }
public string Level { get; set; }
public string ConfigurationId { get; set; }
public string BaseFolder { get; set; }
public string Description { get; set; }
public string UniqueId { get; set; }
public string DataAreaType { get; set; }
}
public class LiamApiVersionInfo
{
public string ProductName { get; set; } = "- unknown -";
public string ProductVersion { get; set; } = "";
public string AssemblyName { get; set; } = "- unknown -";
public string AssemblyVersion { get; set; } = "";
}
public class ProviderCacheEntry
{
public Guid ID;
public Guid ObjectID;
public DateTime ValidUntil;
public cLiamProviderBase Provider;
}
public class LiamDataAreaEntry
{
public string DisplayName { get; set; } = "";
public string UID { get; set; } = "";
public bool SupportsPermissions { get; set; } = false;
}
}

View File

@@ -0,0 +1,399 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
namespace C4IT.LIAM
{
public enum eLiamAccessRoles
{
Owner = 1, Write = 2, Read = 3, Traverse = 4,
ADOwner = 100, // "AD Owner Group"
ADMember = 110, // "AD Member Group"
ExchangeMLMember = 200, // "Mailing List Member"
ExchangeMLOwner = 210, // "Mailing List Owner"
ExchangeSMBFullAccess = 250, // "Shared Mailbox Full Access"
ExchangeSMBSendAs = 260, // "Shared Mailbox Send as"
ExchangeSMBOwner = 270 // "Shared Mailbox Owner"
};
public enum eLiamAccessRoleScopes { Unknown = 0, Universal = 1, Global = 2, DomainLocal = 3 };
public enum eLiamProviderTypes { Unknown = 0, Ntfs = 1, Sharepoint = 2, Matrix42 = 3, MsTeams = 4, ActiveDirectory = 5, Exchange = 6 };
public enum eLiamDataAreaTypes
{
Unknown = 0,
NtfsShare = 101,
NtfsFolder = 102,
MsTeamsTeam = 401,
MsTeamsChannel = 402,
MsTeamsFolder = 403,
ActiveDirectoryGroup = 501,
ExchangeSharedMailbox = 601,
ExchangeDistributionGroup = 602
};
public enum eLiamGroupStrategies { None = 0, Ntfs_AGP = 0, Ntfs_AGDLP = 1 };
public class cLiamConfiguration
{
}
public class cLiamProviderData : ICloneable
{
public eLiamProviderTypes ProviderType { get; set; } = eLiamProviderTypes.Unknown;
public string Domain { get; set; } = "";
public string RootPath { get; set; } = "";
public cLiamCredential Credential { get; set; } = null;
public int MaxDepth { get; set; } = 1;
public eLiamGroupStrategies GroupStrategy { get; set; } = eLiamGroupStrategies.None;
public string DataAreaFilter { get; set; } = "";
public string DataAreaRegEx { get; set; } = "";
public string GroupFilter { get; set; } = "";
public string GroupRegEx { get; set; } = "";
public string GroupPath { get; set; } = "";
public string OwnerGroupGlobal { get; set; } = "";
public string OwnerGroupLocal { get; set; } = "";
public string WriteGroupGlobal { get; set; } = "";
public string WriteGroupLocal { get; set; } = "";
public string ReadGroupGlobal { get; set; } = "";
public string ReadGroupLocal { get; set; } = "";
public string TraverseGroup { get; set; } = "";
public List<cLiamNamingConvention> NamingConventions { get; set; } = new List<cLiamNamingConvention>();
public Dictionary<string, string> CustomTags { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> AdditionalConfiguration { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public static void Copy(cLiamProviderData From, cLiamProviderData To)
{
To.ProviderType = From.ProviderType;
To.Domain = From.Domain;
To.RootPath = From.RootPath;
To.Credential = From.Credential;
To.MaxDepth = From.MaxDepth;
To.GroupStrategy = From.GroupStrategy;
To.DataAreaFilter = From.DataAreaFilter;
To.DataAreaRegEx = From.DataAreaRegEx;
To.GroupFilter = From.GroupFilter;
To.GroupRegEx = From.GroupRegEx;
To.GroupPath = From.GroupPath;
To.CustomTags = From.CustomTags;
To.AdditionalConfiguration = From.AdditionalConfiguration;
To.CustomTags = From.CustomTags;
To.NamingConventions = From.NamingConventions;
}
public cLiamProviderData()
{
}
public cLiamProviderData(cLiamProviderData PD)
{
Copy(PD, this);
}
public object Clone()
{
return new cLiamProviderData(this);
}
public void ReplaceCustomTags()
{
foreach (var customTag in this.CustomTags)
{
var Key = customTag.Key;
foreach (var namingConvention in NamingConventions)
{
if (customTag.Key == "Filesystem_GroupDomainLocalTag" && namingConvention.Scope == eLiamAccessRoleScopes.DomainLocal || customTag.Key == "Filesystem_GroupGlobalTag" && namingConvention.Scope == eLiamAccessRoleScopes.Global)
Key = "SCOPETAG";
else
Key = customTag.Key;
namingConvention.DescriptionTemplate = namingConvention.DescriptionTemplate.Replace($"{{{{{Key}}}}}", customTag.Value);
namingConvention.NamingTemplate = namingConvention.NamingTemplate.Replace($"{{{{{Key}}}}}", customTag.Value);
namingConvention.Wildcard = namingConvention.Wildcard.Replace($"{{{{{Key}}}}}", customTag.Value);
}
GroupFilter = GroupFilter.Replace($"{{{{{Key}}}}}", customTag.Value);
}
}
}
public abstract class cLiamProviderBase : cLiamProviderData
{
public cLiamConfiguration LiamConfiguration { get; private set; }
public cLiamProviderBase(cLiamConfiguration Configuration, cLiamProviderData ProviderData) :
base(ProviderData)
{
LiamConfiguration = Configuration;
}
public abstract Task<List<cLiamDataAreaBase>> getDataAreasAsync(int MaxDepth = -1);
public abstract Task<bool> LogonAsync();
public abstract string GetLastErrorMessage();
public abstract Task<cLiamDataAreaBase> LoadDataArea(string UID);
public static cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var MyPath = Assembly.GetExecutingAssembly()?.Location;
if (MyPath == null)
return null;
MyPath = Path.GetDirectoryName(MyPath);
var DllPath = Path.Combine(MyPath, $"Liam{ProviderData.ProviderType}.dll");
if (!File.Exists(DllPath))
{
LogEntry($"Couldn not found Data Provider library '{DllPath}'", LogLevels.Error);
return null;
}
var assLocal = Assembly.LoadFrom(DllPath);
if (assLocal == null)
{
LogEntry($"Could not load Data Provider library '{DllPath}'", LogLevels.Error);
return null;
}
var TP = assLocal.GetTypes();
var type = assLocal.GetType("C4IT.LIAM.LiamInitializer");
if (type == null)
{
LogEntry($"Could not found class 'C4IT.LIAM.LiamInitializer' in library '{DllPath}'", LogLevels.Error);
return null;
}
var MI = type.GetMethod("CreateInstance");
if (MI == null)
{
LogEntry($"Could not found method 'CreateInstance' in class 'C4IT.LIAM.LiamInitializer' in library '{DllPath}'", LogLevels.Error);
return null;
}
var objDll = MI.Invoke(null, new object[] { LiamConfiguration, ProviderData });
var RetVal = objDll as cLiamProviderBase;
ProviderData.ReplaceCustomTags();
return RetVal;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public abstract Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter);
}
public class cLiamCredential
{
public string Domain { get; set; } = "";
public string Identification { get; set; } = "";
public string Secret { get; set; } = "";
}
public class cLiamNamingConvention
{
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string NamingTemplate { get; set; } = "";
public string DescriptionTemplate { get; set; } = "";
public string Wildcard { get; set; } = "";
public eLiamAccessRoles AccessRole { get; set; } = eLiamAccessRoles.Read;
public eLiamAccessRoleScopes Scope { get; set; } = eLiamAccessRoleScopes.Unknown;
public eLiamProviderTypes? ProviderType { get; set; } = null;
}
public class cLiamDataAreaInfo
{
public string DisplayName { get; protected set; } = null;
public string Description { get; protected set; } = null;
public string OwnerRef { get; set; } = null;
public string UID { get; protected set; } = null;
public string CreatedDate { get; protected set; } = null;
public string TechnicalName { get; protected set; } = "";
public int Level { get; set; } = -1;
public string ParentUID { get; protected set; } = null;
public eLiamDataAreaTypes DataType { get; protected set; } = eLiamDataAreaTypes.Unknown;
public bool SupportsPermissions { get; protected set; } = false;
public bool SupportsOwners { get; protected set; } = false;
}
public abstract class cLiamDataAreaBase : cLiamDataAreaInfo
{
public readonly cLiamProviderBase Provider = null;
public cLiamDataAreaBase(cLiamProviderBase Provider)
{
this.Provider = Provider;
}
public abstract Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1);
public static async Task<List<cLiamDataAreaBase>> getChildrenFromListAsync(List<cLiamDataAreaBase> Items, int Depth = -1)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var RetVal = new List<cLiamDataAreaBase>();
try
{
if (Items == null)
return RetVal;
if (Depth <= 0)
return RetVal;
foreach (var Entry in Items)
{
var Childs = await Entry.getChildrenAsync(Depth + 1);
if (Childs != null)
RetVal.AddRange(Childs);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return RetVal;
}
public virtual async Task<List<cLiamUserInfo>> GetOwnersAsync()
{
await Task.Delay(0);
return null;
}
public virtual async Task<List<cLiamPermissionInfo>> GetPermissionsAsync(bool force)
{
await Task.Delay(0);
return null;
}
public virtual async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
await Task.Delay(0);
return null;
}
public virtual async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
await Task.Delay(0);
return false;
}
}
public class cLiamUserInfo
{
public string DisplayName { get; set; } = null;
public string GivenName { get; set; } = null;
public string SurName { get; set; } = null;
public string UserPrincipalName { get; set; } = null;
public string EMail { get; set; } = null;
public string SID { get; set; } = null;
}
public class cLiamPermissionResult
{
public bool Valid { get; set; } = false;
public string UserReference { get; set; } = null;
}
public class cLiamPermissionInfo
{
public cLiamUserInfo User;
public eLiamAccessRoles AccessRole = eLiamAccessRoles.Read;
public bool OnlyEffective = false;
}
public class DataAreaEntryBase
{
public string DisplayName { get; set; }
public string TechnicalName { get; set; }
public string UID { get; set; }
public string TargetType { get; set; }
}
public class SecurityGroupEntry : DataAreaEntryBase
{
public string Scope { get; set; }
}
public class DataAreaEntry : DataAreaEntryBase
{
public string Parent { get; set; }
public string ParentUID { get; set; }
public string Owner { get; set; }
public string Write { get; set; }
public string Read { get; set; }
public string Traverse { get; set; }
public string CreatedDate { get; set; }
public string Level { get; set; }
public string ConfigurationId { get; set; }
public string BaseFolder { get; set; }
public string Description { get; set; }
public string UniqueId { get; set; }
public string DataAreaType { get; set; }
}
public class LiamApiVersionInfo
{
public string ProductName { get; set; } = "- unknown -";
public string ProductVersion { get; set; } = "";
public string AssemblyName { get; set; } = "- unknown -";
public string AssemblyVersion { get; set; } = "";
}
public class ProviderCacheEntry
{
public Guid ID;
public Guid ObjectID;
public DateTime ValidUntil;
public cLiamProviderBase Provider;
}
public class LiamDataAreaEntry
{
public string DisplayName { get; set; } = "";
public string UID { get; set; } = "";
public bool SupportsPermissions { get; set; } = false;
}
}

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{3531C9E6-CF6E-458E-B604-4A5A8D1C7AB0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LiamBaseClasses</RootNamespace>
<AssemblyName>LiamBaseClasses</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>false</Deterministic>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="C4IT.LIAM.Base.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LiamHelper\LiamHelper.csproj">
<Project>{6b0e73a6-f918-42d5-9525-d59d4d16283d}</Project>
<Name>LiamHelper</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}

View File

@@ -0,0 +1,11 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("LIAM support library for basic classes & interfaces")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("3531c9e6-cf6e-458e-b604-4a5a8d1c7ab0")]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]

View File

@@ -0,0 +1 @@
3908855f9633bf09d020c2efbd1fa153a9ad8a91b6e76b39255ee0403b9907f8

View File

@@ -0,0 +1,10 @@
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Debug\LiamBaseClasses.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Debug\LiamBaseClasses.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Debug\LiamHelper.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Debug\Newtonsoft.Json.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Debug\LiamHelper.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Debug\LiamBaseClasses.csproj.AssemblyReference.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Debug\LiamBaseClasses.csproj.CoreCompileInputs.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Debug\LiamBase.24B0A51E.Up2Date
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Debug\LiamBaseClasses.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Debug\LiamBaseClasses.pdb

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]

View File

@@ -0,0 +1 @@
77f86ea35cc63f1fe117fbff3ddf1a527617a8ddb75413eb5ef7f6b4cf6c8399

View File

@@ -0,0 +1,10 @@
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Release\LiamBaseClasses.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Release\LiamBaseClasses.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Release\LiamHelper.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Release\Newtonsoft.Json.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\bin\Release\LiamHelper.pdb
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Release\LiamBaseClasses.csproj.AssemblyReference.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Release\LiamBaseClasses.csproj.CoreCompileInputs.cache
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Release\LiamBase.24B0A51E.Up2Date
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Release\LiamBaseClasses.dll
C:\Workspace\C4IT DEV LIAM WEB Service\LiamBaseClasses\obj\Release\LiamBaseClasses.pdb

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,898 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using C4IT.Matrix42.ServerInfo;
using System.Text.RegularExpressions;
using LiamNtfs;
using System.DirectoryServices;
using System.Security.Principal;
namespace C4IT.LIAM
{
public class cLiamProviderExchange : cLiamProviderBase
{
public static Guid exchangeModuleId = new Guid("A1E213C3-6517-EA11-4881-000C2980FD95");
public readonly ExchangeManager exchangeManager;
internal readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
private string exchangeUri;
private PSCredential credential;
private string organizationalUnit;
private string lastErrorMessage;
private bool isLoggedOn = false;
public cLiamProviderExchange(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
base(LiamConfiguration, ProviderData)
{
exchangeUri = ProviderData.RootPath;
if (!string.IsNullOrEmpty(ProviderData.GroupPath))
organizationalUnit = ProviderData.GroupPath;
else
organizationalUnit = ProviderData.Domain;
// Credential erstellen
var securePassword = new SecureString();
foreach (char c in ProviderData.Credential.Secret)
{
securePassword.AppendChar(c);
}
credential = new PSCredential(ProviderData.Credential.Identification, securePassword);
// ExchangeManager initialisieren
exchangeManager = new ExchangeManager(this, exchangeUri, credential, ProviderData.Domain, organizationalUnit);
// AD-Zugriff initialisieren
var LI = new cNtfsLogonInfo()
{
Domain = Domain,
User = Credential?.Identification,
UserSecret = Credential?.Secret,
TargetGroupPath = this.GroupPath
};
// Asynchrone Initialisierung starten
_ = activeDirectoryBase.LogonAsync(LI).ConfigureAwait(false);
}
/// <summary>
/// Extrahiert den GUID-Wert aus den Properties.
/// Zunächst wird versucht, "objectGUID" zu lesen ist dieser leer, wird "GUID" verwendet.
/// </summary>
internal static string ExtractObjectGuid(dynamic properties)
{
// Erstversuch: objectGUID (wie in AD)
var value = properties["objectGUID"]?.Value;
if (value == null || string.IsNullOrEmpty(value.ToString()))
{
// Alternative: GUID (wie von den Exchange-Cmdlets zurückgegeben)
value = properties["GUID"]?.Value;
}
if (value is byte[] guidBytes)
return new Guid(guidBytes).ToString();
if (value is Guid guid)
return guid.ToString();
return value?.ToString() ?? string.Empty;
}
public override async Task<bool> LogonAsync()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid)
{
LogEntry("Error: License not valid or Exchange module not licensed", LogLevels.Error);
lastErrorMessage = "License not valid or Exchange module not licensed";
return false;
}
try
{
var testMailboxes = exchangeManager.GetSharedMailboxes("Name -like '*'");
if (testMailboxes != null)
{
LogEntry("Successfully connected to Exchange", LogLevels.Info);
isLoggedOn = true;
return true;
}
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Failed to connect to Exchange: {ex.Message}";
isLoggedOn = false;
return false;
}
lastErrorMessage = "Unknown error connecting to Exchange";
return false;
}
catch (Exception E)
{
LogException(E);
lastErrorMessage = $"Exception during Exchange logon: {E.Message}";
return false;
}
finally
{
LogMethodEnd(CM);
}
}
public override string GetLastErrorMessage()
{
return lastErrorMessage;
}
public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int MaxDepth = -1)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid)
{
LogEntry("Error: License not valid or Exchange module not licensed", LogLevels.Error);
return new List<cLiamDataAreaBase>();
}
if (!isLoggedOn && !await LogonAsync())
return null;
var DataAreas = new List<cLiamDataAreaBase>();
// Shared Mailboxes
try
{
var sharedMailboxes = exchangeManager.GetSharedMailboxes();
foreach (var mailbox in sharedMailboxes)
{
var displayName = mailbox.Properties["DisplayName"]?.Value?.ToString();
var alias = mailbox.Properties["Alias"]?.Value?.ToString();
var primarySmtpAddress = mailbox.Properties["PrimarySmtpAddress"]?.Value?.ToString();
var objectGuid = ExtractObjectGuid(mailbox.Properties);
// Filterung via Regex
if (!string.IsNullOrEmpty(this.DataAreaRegEx) &&
!Regex.Match(displayName, this.DataAreaRegEx).Success)
continue;
var exchangeMailbox = new cLiamExchangeSharedMailbox(this, displayName, primarySmtpAddress, alias, objectGuid);
DataAreas.Add(exchangeMailbox);
}
}
catch (Exception ex)
{
LogException(ex);
}
// Distribution Groups
try
{
var distributionGroups = exchangeManager.GetDistributionGroups();
foreach (var group in distributionGroups)
{
var displayName = group.Properties["DisplayName"]?.Value?.ToString();
var alias = group.Properties["Alias"]?.Value?.ToString();
var primarySmtpAddress = group.Properties["PrimarySmtpAddress"]?.Value?.ToString();
var objectGuid = ExtractObjectGuid(group.Properties);
if (!string.IsNullOrEmpty(this.DataAreaRegEx) &&
!Regex.Match(displayName, this.DataAreaRegEx).Success)
continue;
var exchangeGroup = new cLiamExchangeDistributionGroup(this, displayName, primarySmtpAddress, alias, objectGuid);
DataAreas.Add(exchangeGroup);
}
}
catch (Exception ex)
{
LogException(ex);
}
return DataAreas;
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return null;
}
public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var dataType = cLIAMHelper.getUidItem(ref UID);
if (string.IsNullOrEmpty(dataType))
return null;
if (!int.TryParse(dataType, out int dataTypeInt))
return null;
var primarySmtpAddress = cLIAMHelper.getUidItem(ref UID);
if (string.IsNullOrEmpty(primarySmtpAddress))
return null;
if (!isLoggedOn && !await LogonAsync())
return null;
switch ((eLiamDataAreaTypes)dataTypeInt)
{
case eLiamDataAreaTypes.ExchangeSharedMailbox:
return await cLiamExchangeSharedMailbox.Load(this, primarySmtpAddress);
case eLiamDataAreaTypes.ExchangeDistributionGroup:
return await cLiamExchangeDistributionGroup.Load(this, primarySmtpAddress);
default:
return null;
}
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid)
{
LogEntry("Error: License not valid or Exchange module not licensed", LogLevels.Error);
return new List<cLiamDataAreaBase>();
}
if (!isLoggedOn && !await LogonAsync())
return null;
var securityGroups = new List<cLiamDataAreaBase>();
try
{
var groups = exchangeManager.GetSecurityGroups(groupFilter);
foreach (var group in groups)
{
var displayName = group.Properties["DisplayName"]?.Value?.ToString();
var sid = group.Properties["Sid"]?.Value?.ToString();
var dn = group.Properties["DistinguishedName"]?.Value?.ToString();
var objectGuid = ExtractObjectGuid(group.Properties);
if (!string.IsNullOrEmpty(this.GroupRegEx) &&
!Regex.Match(displayName, this.GroupRegEx).Success)
continue;
var securityGroup = new cLiamExchangeSecurityGroup(this, displayName, sid, dn, objectGuid);
securityGroups.Add(securityGroup);
}
}
catch (Exception ex)
{
LogException(ex);
}
return securityGroups;
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return null;
}
// Hilfsmethoden zur Interaktion mit Exchange
internal async Task<bool> AddMemberToGroup(string groupName, string member, bool isSharedMailbox)
{
try
{
if (isSharedMailbox)
exchangeManager.AddMailboxPermission(groupName, member);
else
exchangeManager.AddMemberToDistributionGroup(groupName, member);
return true;
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Error adding member to group: {ex.Message}";
return false;
}
}
internal async Task<bool> RemoveMemberFromGroup(string groupName, string member, bool isSharedMailbox)
{
try
{
if (isSharedMailbox)
exchangeManager.RemoveMailboxPermission(groupName, member);
else
exchangeManager.RemoveMemberFromDistributionGroup(groupName, member);
return true;
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Error removing member from group: {ex.Message}";
return false;
}
}
public async Task<cLiamExchangeSecurityGroup> GetManagedByGroup(string groupDn)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (activeDirectoryBase.adContext == null)
{
LogEntry("Active Directory context not initialized", LogLevels.Error);
return null;
}
string ldapPath = $"LDAP://{groupDn}";
DirectoryEntry groupEntry = null;
try
{
groupEntry = new DirectoryEntry(
ldapPath,
this.Credential?.Identification,
this.Credential?.Secret,
AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
if (groupEntry.Properties.Contains("managedBy") && groupEntry.Properties["managedBy"].Value != null)
{
string managedByDn = groupEntry.Properties["managedBy"].Value.ToString();
string managedByLdapPath = $"LDAP://{managedByDn}";
using (DirectoryEntry managedByEntry = new DirectoryEntry(
managedByLdapPath,
this.Credential?.Identification,
this.Credential?.Secret,
AuthenticationTypes.Secure | AuthenticationTypes.Sealing))
{
if (managedByEntry.SchemaClassName == "group")
{
byte[] sidBytes = (byte[])managedByEntry.Properties["objectSid"].Value;
SecurityIdentifier sid = new SecurityIdentifier(sidBytes, 0);
string displayName = managedByEntry.Properties["displayName"]?.Value?.ToString()
?? managedByEntry.Properties["name"]?.Value?.ToString();
return new cLiamExchangeSecurityGroup(this, displayName, sid.Value, managedByDn, sid.Value);
}
}
}
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
groupEntry?.Dispose();
}
return null;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
}
public class cLiamExchangeSecurityGroup : cLiamDataAreaBase
{
public new readonly cLiamProviderExchange Provider = null;
public readonly string sid = null;
public readonly string dn = null;
// objectGuid wird nun als TechnicalName genutzt
public cLiamExchangeSecurityGroup(cLiamProviderExchange Provider, string displayName, string sid, string dn, string objectGuid) : base(Provider)
{
this.Provider = Provider;
this.TechnicalName = displayName;
this.DisplayName = displayName;
this.UID = sid;
this.sid = sid;
this.dn = dn;
}
public override Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
return Task.FromResult(new List<cLiamDataAreaBase>());
}
}
public class cLiamExchangeSharedMailbox : cLiamDataAreaBase
{
public new readonly cLiamProviderExchange Provider = null;
public readonly string PrimarySmtpAddress = null;
public readonly string Alias = null;
public string OwnerGroupIdentifier = "S-1-0-0";
public string FullAccessGroupSid = "S-1-0-0";
public string SendAsGroupSid = "S-1-0-0";
// objectGuid wird für TechnicalName genutzt
public cLiamExchangeSharedMailbox(cLiamProviderExchange Provider, string displayName, string primarySmtpAddress, string alias, string objectGuid) : base(Provider)
{
this.Provider = Provider;
this.TechnicalName = objectGuid;
this.DisplayName = displayName;
this.PrimarySmtpAddress = primarySmtpAddress;
this.Alias = alias;
this.UID = getUID(primarySmtpAddress);
this.Level = 0;
this.DataType = eLiamDataAreaTypes.ExchangeSharedMailbox;
this.SupportsOwners = true;
this.SupportsPermissions = true;
_ = assignPermissionGroups(Provider).ConfigureAwait(false);
}
internal static string getUID(string primarySmtpAddress)
{
return $"{(int)eLiamDataAreaTypes.ExchangeSharedMailbox}|{primarySmtpAddress}";
}
public static async Task<cLiamExchangeSharedMailbox> Load(cLiamProviderExchange Provider, string primarySmtpAddress)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var mailbox = Provider.exchangeManager.GetSharedMailboxByAddress(primarySmtpAddress);
if (mailbox == null)
return null;
var displayName = mailbox.Properties["DisplayName"]?.Value?.ToString();
var alias = mailbox.Properties["Alias"]?.Value?.ToString();
var objectGuid = cLiamProviderExchange.ExtractObjectGuid(mailbox.Properties);
return new cLiamExchangeSharedMailbox(Provider, displayName, primarySmtpAddress, alias, objectGuid);
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var members = Provider.exchangeManager.GetMailboxPermissionMembers(this.TechnicalName);
if (members == null)
return new List<cLiamUserInfo>();
var ownersList = new List<cLiamUserInfo>();
foreach (var member in members)
{
var userInfo = new cLiamUserInfo
{
DisplayName = member.Properties["DisplayName"]?.Value?.ToString(),
UserPrincipalName = member.Properties["UserPrincipalName"]?.Value?.ToString(),
EMail = member.Properties["PrimarySmtpAddress"]?.Value?.ToString(),
SID = member.Properties["Sid"]?.Value?.ToString()
};
ownersList.Add(userInfo);
}
return ownersList;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
bool success = false;
switch (Role)
{
case eLiamAccessRoles.Owner:
success = await Provider.AddMemberToGroup(this.DisplayName, User.UserPrincipalName, true);
break;
case eLiamAccessRoles.Write:
success = await Provider.exchangeManager.AddSendAsPermission(this.DisplayName, User.UserPrincipalName);
break;
default:
LogEntry($"Unsupported permission role for Exchange mailbox: {Role}", LogLevels.Warning);
return new cLiamPermissionResult { Valid = false };
}
return new cLiamPermissionResult
{
Valid = success,
UserReference = User.UserPrincipalName
};
}
catch (Exception E)
{
LogException(E);
return new cLiamPermissionResult { Valid = false };
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
bool success = false;
switch (Role)
{
case eLiamAccessRoles.Owner:
success = await Provider.RemoveMemberFromGroup(this.DisplayName, User.UserPrincipalName, true);
break;
case eLiamAccessRoles.Write:
success = await Provider.exchangeManager.RemoveSendAsPermission(this.DisplayName, User.UserPrincipalName);
break;
default:
LogEntry($"Unsupported permission role for Exchange mailbox: {Role}", LogLevels.Warning);
return false;
}
return success;
}
catch (Exception E)
{
LogException(E);
return false;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
return new List<cLiamDataAreaBase>();
}
private async Task assignPermissionGroups(cLiamProviderExchange Provider)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var fullAccessNamingConvention = Provider.NamingConventions.FirstOrDefault(i =>
i.AccessRole == eLiamAccessRoles.ExchangeSMBFullAccess);
var sendAsNamingConvention = Provider.NamingConventions.FirstOrDefault(i =>
i.AccessRole == eLiamAccessRoles.ExchangeSMBSendAs);
if (fullAccessNamingConvention == null || sendAsNamingConvention == null)
{
LogEntry("Naming conventions for Exchange mailbox permissions not found", LogLevels.Warning);
return;
}
try
{
var fullAccessPermissions = Provider.exchangeManager.GetFullAccessPermissionGroups(this.TechnicalName);
if (fullAccessPermissions != null)
{
foreach (var permission in fullAccessPermissions)
{
string samAccountName = permission.Properties["SamAccountName"]?.Value?.ToString();
string sid = permission.Properties["Sid"]?.Value?.ToString();
if (!string.IsNullOrEmpty(samAccountName) && !string.IsNullOrEmpty(sid))
{
//if (Regex.IsMatch(samAccountName, fullAccessNamingConvention.Wildcard, RegexOptions.IgnoreCase))
{
this.FullAccessGroupSid = sid;
LogEntry($"Found FullAccess group {samAccountName} (SID: {sid}) for mailbox {this.DisplayName}", LogLevels.Debug);
string dn = permission.Properties["DistinguishedName"]?.Value?.ToString();
if (!string.IsNullOrEmpty(dn))
{
var managedByGroup = await Provider.GetManagedByGroup(dn);
if (managedByGroup != null)
{
this.OwnerGroupIdentifier = managedByGroup.sid;
LogEntry($"Found owner group {managedByGroup.TechnicalName} (SID: {managedByGroup.sid}) for mailbox {this.DisplayName}", LogLevels.Debug);
}
}
}
}
}
}
var sendAsPermissions = Provider.exchangeManager.GetSendAsPermissionGroups(this.TechnicalName);
if (sendAsPermissions != null)
{
foreach (var permission in sendAsPermissions)
{
string recipientType = permission.Properties["RecipientType"]?.Value?.ToString();
string samAccountName = permission.Properties["SamAccountName"]?.Value?.ToString();
string sid = permission.Properties["Sid"]?.Value?.ToString();
if (!string.IsNullOrEmpty(samAccountName) && !string.IsNullOrEmpty(sid))
{
//if (Regex.IsMatch(samAccountName, sendAsNamingConvention.Wildcard, RegexOptions.IgnoreCase))
{
this.SendAsGroupSid = sid;
LogEntry($"Found SendAs group {samAccountName} (SID: {sid}) for mailbox {this.DisplayName}", LogLevels.Debug);
}
}
}
}
}
catch (Exception ex)
{
LogException(ex);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
}
}
public class cLiamExchangeDistributionGroup : cLiamDataAreaBase
{
public new readonly cLiamProviderExchange Provider = null;
public readonly string PrimarySmtpAddress = null;
public readonly string Alias = null;
public string OwnerGroupIdentifier = "S-1-0-0";
public string MemberGroupSid = "S-1-0-0";
// objectGuid wird als TechnicalName gesetzt
public cLiamExchangeDistributionGroup(cLiamProviderExchange Provider, string displayName, string primarySmtpAddress, string alias, string objectGuid) : base(Provider)
{
this.Provider = Provider;
this.TechnicalName = objectGuid;
this.DisplayName = displayName;
this.PrimarySmtpAddress = primarySmtpAddress;
this.Alias = alias;
this.UID = getUID(primarySmtpAddress);
this.Level = 0;
this.DataType = eLiamDataAreaTypes.ExchangeDistributionGroup;
this.SupportsOwners = true;
this.SupportsPermissions = true;
_ = assignPermissionGroups(Provider).ConfigureAwait(false);
}
internal static string getUID(string primarySmtpAddress)
{
return $"{(int)eLiamDataAreaTypes.ExchangeDistributionGroup}|{primarySmtpAddress}";
}
public static async Task<cLiamExchangeDistributionGroup> Load(cLiamProviderExchange Provider, string primarySmtpAddress)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var group = Provider.exchangeManager.GetDistributionGroupByAddress(primarySmtpAddress);
if (group == null)
return null;
var displayName = group.Properties["DisplayName"]?.Value?.ToString();
var alias = group.Properties["Alias"]?.Value?.ToString();
var objectGuid = cLiamProviderExchange.ExtractObjectGuid(group.Properties);
return new cLiamExchangeDistributionGroup(Provider, displayName, primarySmtpAddress, alias, objectGuid);
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var members = Provider.exchangeManager.GetDistributionGroupMembers(this.TechnicalName);
if (members == null)
return new List<cLiamUserInfo>();
var membersList = new List<cLiamUserInfo>();
foreach (var member in members)
{
var userInfo = new cLiamUserInfo
{
DisplayName = member.Properties["DisplayName"]?.Value?.ToString(),
UserPrincipalName = member.Properties["UserPrincipalName"]?.Value?.ToString(),
EMail = member.Properties["PrimarySmtpAddress"]?.Value?.ToString(),
SID = member.Properties["Sid"]?.Value?.ToString()
};
membersList.Add(userInfo);
}
return membersList;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (Role != eLiamAccessRoles.Owner)
{
LogEntry($"Only Owner role is supported for distribution groups, requested: {Role}", LogLevels.Warning);
return new cLiamPermissionResult { Valid = false };
}
bool success = await Provider.AddMemberToGroup(this.DisplayName, User.UserPrincipalName, false);
return new cLiamPermissionResult
{
Valid = success,
UserReference = User.UserPrincipalName
};
}
catch (Exception E)
{
LogException(E);
return new cLiamPermissionResult { Valid = false };
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (Role != eLiamAccessRoles.Owner)
{
LogEntry($"Only Owner role is supported for distribution groups, requested: {Role}", LogLevels.Warning);
return false;
}
return await Provider.RemoveMemberFromGroup(this.DisplayName, User.UserPrincipalName, false);
}
catch (Exception E)
{
LogException(E);
return false;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
return new List<cLiamDataAreaBase>();
}
private async Task assignPermissionGroups(cLiamProviderExchange Provider)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var namingConvention = Provider.NamingConventions
.FirstOrDefault(nc => nc.AccessRole == eLiamAccessRoles.ExchangeMLMember);
if (namingConvention == null)
{
LogEntry("Naming convention for DL-Member not found", LogLevels.Warning);
return;
}
try
{
// ruft alle Gruppen auf dem DL ab
var memberGroups = Provider.exchangeManager
.GetDistributionGroupMembers(this.TechnicalName);
foreach (var group in memberGroups)
{
var sam = group.Properties["SamAccountName"]?.Value?.ToString();
var sid = group.Properties["Sid"]?.Value?.ToString();
if (string.IsNullOrEmpty(sam) || string.IsNullOrEmpty(sid))
continue;
// falls gewünscht: Filter nach Namenskonvention
// if (!Regex.IsMatch(sam, namingConvention.Wildcard, RegexOptions.IgnoreCase))
// continue;
// hier beispielsweise in eine List<string> MemberGroupSids aufnehmen
this.MemberGroupSid = sid;
LogEntry($"Found DL-member group {sam} (SID: {sid}) for distribution list {this.DisplayName}", LogLevels.Debug);
// optional: falls die Gruppe ein ManagedBy hat
var dn = group.Properties["DistinguishedName"]?.Value?.ToString();
if (!string.IsNullOrEmpty(dn))
{
var mgr = await Provider.GetManagedByGroup(dn);
if (mgr != null)
{
this.OwnerGroupIdentifier = mgr.sid;
LogEntry($"Found owner group {mgr.TechnicalName} (SID: {mgr.sid}) for distribution list {this.DisplayName}", LogLevels.Debug);
}
}
}
}
catch (Exception ex)
{
LogException(ex);
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
}
}
}

View File

@@ -0,0 +1,726 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using C4IT.Matrix42.ServerInfo;
namespace C4IT.LIAM
{
public static class LiamInitializer
{
static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData)
{
return new cLiamProviderExchange(LiamConfiguration, ProviderData);
}
}
public class cLiamProviderExchange : cLiamProviderBase
{
public static Guid exchangeModuleId = new Guid("A1E213C3-6517-EA11-4881-000C2980FD95");
internal readonly ExchangeManager exchangeManager;
private string exchangeUri;
private PSCredential credential;
private string organizationalUnit;
private string lastErrorMessage;
private bool isLoggedOn = false;
public cLiamProviderExchange(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) :
base(LiamConfiguration, ProviderData)
{
exchangeUri = ProviderData.RootPath;
if (!string.IsNullOrEmpty(ProviderData.GroupPath))
organizationalUnit = ProviderData.GroupPath;
else
organizationalUnit = ProviderData.Domain;
// Use the credential from the provider data
var securePassword = new SecureString();
foreach (char c in ProviderData.Credential.Secret)
{
securePassword.AppendChar(c);
}
credential = new PSCredential(ProviderData.Credential.Identification, securePassword);
// Create the Exchange manager
exchangeManager = new ExchangeManager(exchangeUri, credential, organizationalUnit);
}
public override async Task<bool> LogonAsync()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid /*|| !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(exchangeModuleId)*/)
{
LogEntry($"Error: License not valid or Exchange module not licensed", LogLevels.Error);
lastErrorMessage = "License not valid or Exchange module not licensed";
return false;
}
// Test connection by getting a simple list
try
{
var testMailboxes = exchangeManager.GetSharedMailboxes("Name -like '*'");
if (testMailboxes != null)
{
LogEntry($"Successfully connected to Exchange", LogLevels.Info);
isLoggedOn = true;
return true;
}
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Failed to connect to Exchange: {ex.Message}";
isLoggedOn = false;
return false;
}
lastErrorMessage = "Unknown error connecting to Exchange";
return false;
}
catch (Exception E)
{
LogException(E);
lastErrorMessage = $"Exception during Exchange logon: {E.Message}";
return false;
}
finally
{
LogMethodEnd(CM);
}
}
public override string GetLastErrorMessage()
{
return lastErrorMessage;
}
public override async Task<List<cLiamDataAreaBase>> getDataAreasAsync(int MaxDepth = -1)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid /*|| !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(exchangeModuleId)*/)
{
LogEntry($"Error: License not valid or Exchange module not licensed", LogLevels.Error);
return new List<cLiamDataAreaBase>();
}
if (!isLoggedOn && !await LogonAsync())
return null;
var DataAreas = new List<cLiamDataAreaBase>();
// Get Shared Mailboxes
try
{
var sharedMailboxes = exchangeManager.GetSharedMailboxes();
foreach (var mailbox in sharedMailboxes)
{
var displayName = mailbox.Properties["DisplayName"]?.Value?.ToString();
var alias = mailbox.Properties["Alias"]?.Value?.ToString();
var primarySmtpAddress = mailbox.Properties["PrimarySmtpAddress"]?.Value?.ToString();
// Skip if it doesn't match the regex filter (if provided)
if (!string.IsNullOrEmpty(this.DataAreaRegEx) &&
!System.Text.RegularExpressions.Regex.Match(displayName, this.DataAreaRegEx).Success)
continue;
var exchangeMailbox = new cLiamExchangeSharedMailbox(this, displayName, primarySmtpAddress, alias);
DataAreas.Add(exchangeMailbox);
}
}
catch (Exception ex)
{
LogException(ex);
}
// Get Distribution Groups
try
{
var distributionGroups = exchangeManager.GetDistributionGroups();
foreach (var group in distributionGroups)
{
var displayName = group.Properties["DisplayName"]?.Value?.ToString();
var alias = group.Properties["Alias"]?.Value?.ToString();
var primarySmtpAddress = group.Properties["PrimarySmtpAddress"]?.Value?.ToString();
// Skip if it doesn't match the regex filter (if provided)
if (!string.IsNullOrEmpty(this.DataAreaRegEx) &&
!System.Text.RegularExpressions.Regex.Match(displayName, this.DataAreaRegEx).Success)
continue;
var exchangeGroup = new cLiamExchangeDistributionGroup(this, displayName, primarySmtpAddress, alias);
DataAreas.Add(exchangeGroup);
}
}
catch (Exception ex)
{
LogException(ex);
}
return DataAreas;
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return null;
}
public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var dataType = cLIAMHelper.getUidItem(ref UID);
if (string.IsNullOrEmpty(dataType))
return null;
int dataTypeInt;
if (!int.TryParse(dataType, out dataTypeInt))
return null;
var primarySmtpAddress = cLIAMHelper.getUidItem(ref UID);
if (string.IsNullOrEmpty(primarySmtpAddress))
return null;
if (!isLoggedOn && !await LogonAsync())
return null;
switch ((eLiamDataAreaTypes)dataTypeInt)
{
case eLiamDataAreaTypes.ExchangeSharedMailbox:
return await cLiamExchangeSharedMailbox.Load(this, primarySmtpAddress);
case eLiamDataAreaTypes.ExchangeDistributionGroup:
return await cLiamExchangeDistributionGroup.Load(this, primarySmtpAddress);
default:
return null;
}
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
if (!cC4ITLicenseM42ESM.Instance.IsValid /*|| !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(exchangeModuleId)*/)
{
LogEntry($"Error: License not valid or Exchange module not licensed", LogLevels.Error);
return new List<cLiamDataAreaBase>();
}
if (!isLoggedOn && !await LogonAsync())
return null;
// For Exchange, we need to use the same AD security groups as from the AD provider
// This is just a placeholder implementation to return some groups from the exchange environment
var securityGroups = new List<cLiamDataAreaBase>();
try
{
// Use the Exchange PowerShell to get AD groups
var groups = exchangeManager.GetSecurityGroups(groupFilter);
foreach (var group in groups)
{
var displayName = group.Properties["DisplayName"]?.Value?.ToString();
var sid = group.Properties["Sid"]?.Value?.ToString();
var dn = group.Properties["DistinguishedName"]?.Value?.ToString();
// Skip if it doesn't match the regex filter (if provided)
if (!string.IsNullOrEmpty(this.GroupRegEx) &&
!System.Text.RegularExpressions.Regex.Match(displayName, this.GroupRegEx).Success)
continue;
var securityGroup = new cLiamExchangeSecurityGroup(this, displayName, sid, dn);
securityGroups.Add(securityGroup);
}
}
catch (Exception ex)
{
LogException(ex);
}
return securityGroups;
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
return null;
}
// Helper methods to interact with Exchange
internal async Task<bool> CreateSharedMailbox(string displayName, string alias, string ownerGroupName, string writeGroupName)
{
try
{
exchangeManager.CreateSharedMailbox(displayName, alias, null);
// Create the owner (FullAccess) and write (SendAs) groups if they don't exist already
exchangeManager.CreateSecurityGroup(ownerGroupName, $"{displayName} Full Access");
exchangeManager.CreateSecurityGroup(writeGroupName, $"{displayName} Send As");
// Grant permissions to the groups
exchangeManager.AddMailboxPermission(displayName, ownerGroupName, "FullAccess");
exchangeManager.AddSendAsPermission(displayName, writeGroupName);
return true;
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Error creating shared mailbox: {ex.Message}";
return false;
}
}
internal async Task<bool> CreateDistributionGroup(string displayName, string alias, List<string> initialMembers = null)
{
try
{
exchangeManager.CreateDistributionGroup(displayName, alias, initialMembers);
return true;
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Error creating distribution group: {ex.Message}";
return false;
}
}
internal async Task<bool> AddMemberToGroup(string groupName, string member, bool isSharedMailbox)
{
try
{
if (isSharedMailbox)
{
exchangeManager.AddMailboxPermission(groupName, member);
}
else
{
exchangeManager.AddMemberToDistributionGroup(groupName, member);
}
return true;
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Error adding member to group: {ex.Message}";
return false;
}
}
internal async Task<bool> RemoveMemberFromGroup(string groupName, string member, bool isSharedMailbox)
{
try
{
if (isSharedMailbox)
{
exchangeManager.RemoveMailboxPermission(groupName, member);
}
else
{
exchangeManager.RemoveMemberFromDistributionGroup(groupName, member);
}
return true;
}
catch (Exception ex)
{
LogException(ex);
lastErrorMessage = $"Error removing member from group: {ex.Message}";
return false;
}
}
}
public class cLiamExchangeSecurityGroup : cLiamDataAreaBase
{
public new readonly cLiamProviderExchange Provider = null;
public readonly string sid = null;
public readonly string dn = null;
public cLiamExchangeSecurityGroup(cLiamProviderExchange Provider, string displayName, string sid, string dn) : base(Provider)
{
this.Provider = Provider;
this.TechnicalName = displayName;
this.UID = sid;
this.sid = sid;
this.dn = dn;
}
public override Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
// Security groups don't have children in this context
return Task.FromResult(new List<cLiamDataAreaBase>());
}
}
public class cLiamExchangeSharedMailbox : cLiamDataAreaBase
{
public new readonly cLiamProviderExchange Provider = null;
public readonly string PrimarySmtpAddress = null;
public readonly string Alias = null;
public string OwnerGroupIdentifier = null; // Full Access group
public string WriteGroupIdentifier = null; // Send As group
public cLiamExchangeSharedMailbox(cLiamProviderExchange Provider, string displayName, string primarySmtpAddress, string alias) : base(Provider)
{
this.Provider = Provider;
this.TechnicalName = displayName;
this.DisplayName = displayName;
this.PrimarySmtpAddress = primarySmtpAddress;
this.Alias = alias;
this.UID = getUID(primarySmtpAddress);
this.Level = 0;
this.DataType = eLiamDataAreaTypes.ExchangeSharedMailbox;
this.SupportsOwners = true;
this.SupportsPermissions = true;
// Set standard naming convention for access groups
// These will be resolved to actual groups later as needed
this.OwnerGroupIdentifier = $"EXCH_FA_{alias}"; // Full Access group
this.WriteGroupIdentifier = $"EXCH_SA_{alias}"; // Send As group
}
internal static string getUID(string primarySmtpAddress)
{
return $"{(int)eLiamDataAreaTypes.ExchangeSharedMailbox}|{primarySmtpAddress}";
}
public static async Task<cLiamExchangeSharedMailbox> Load(cLiamProviderExchange Provider, string primarySmtpAddress)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
// Get the mailbox details from Exchange
var mailbox = Provider.exchangeManager.GetSharedMailboxByAddress(primarySmtpAddress);
if (mailbox == null)
return null;
var displayName = mailbox.Properties["DisplayName"]?.Value?.ToString();
var alias = mailbox.Properties["Alias"]?.Value?.ToString();
return new cLiamExchangeSharedMailbox(Provider, displayName, primarySmtpAddress, alias);
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
// Get members of the Full Access group
var members = Provider.exchangeManager.GetMailboxPermissionMembers(this.DisplayName);
if (members == null)
return new List<cLiamUserInfo>();
var ownersList = new List<cLiamUserInfo>();
foreach (var member in members)
{
var userInfo = new cLiamUserInfo
{
DisplayName = member.Properties["DisplayName"]?.Value?.ToString(),
UserPrincipalName = member.Properties["UserPrincipalName"]?.Value?.ToString(),
EMail = member.Properties["PrimarySmtpAddress"]?.Value?.ToString(),
SID = member.Properties["Sid"]?.Value?.ToString()
};
ownersList.Add(userInfo);
}
return ownersList;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
bool success = false;
switch (Role)
{
case eLiamAccessRoles.Owner:
// Add to Full Access group
success = await Provider.AddMemberToGroup(this.DisplayName, User.UserPrincipalName, true);
break;
case eLiamAccessRoles.Write:
// Add Send As permission
success = await Provider.exchangeManager.AddSendAsPermission(this.DisplayName, User.UserPrincipalName);
break;
default:
LogEntry($"Unsupported permission role for Exchange mailbox: {Role}", LogLevels.Warning);
return new cLiamPermissionResult { Valid = false };
}
return new cLiamPermissionResult
{
Valid = success,
UserReference = User.UserPrincipalName
};
}
catch (Exception E)
{
LogException(E);
return new cLiamPermissionResult { Valid = false };
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
bool success = false;
switch (Role)
{
case eLiamAccessRoles.Owner:
// Remove from Full Access group
success = await Provider.RemoveMemberFromGroup(this.DisplayName, User.UserPrincipalName, true);
break;
case eLiamAccessRoles.Write:
// Remove Send As permission
success = await Provider.exchangeManager.RemoveSendAsPermission(this.DisplayName, User.UserPrincipalName);
break;
default:
LogEntry($"Unsupported permission role for Exchange mailbox: {Role}", LogLevels.Warning);
return false;
}
return success;
}
catch (Exception E)
{
LogException(E);
return false;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
// Shared mailboxes don't have children in this context
return new List<cLiamDataAreaBase>();
}
}
public class cLiamExchangeDistributionGroup : cLiamDataAreaBase
{
public new readonly cLiamProviderExchange Provider = null;
public readonly string PrimarySmtpAddress = null;
public readonly string Alias = null;
public cLiamExchangeDistributionGroup(cLiamProviderExchange Provider, string displayName, string primarySmtpAddress, string alias) : base(Provider)
{
this.Provider = Provider;
this.TechnicalName = displayName;
this.DisplayName = displayName;
this.PrimarySmtpAddress = primarySmtpAddress;
this.Alias = alias;
this.UID = getUID(primarySmtpAddress);
this.Level = 0;
this.DataType = eLiamDataAreaTypes.ExchangeDistributionGroup;
this.SupportsOwners = true;
this.SupportsPermissions = true;
}
internal static string getUID(string primarySmtpAddress)
{
return $"{(int)eLiamDataAreaTypes.ExchangeDistributionGroup}|{primarySmtpAddress}";
}
public static async Task<cLiamExchangeDistributionGroup> Load(cLiamProviderExchange Provider, string primarySmtpAddress)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
// Get the group details from Exchange
var group = Provider.exchangeManager.GetDistributionGroupByAddress(primarySmtpAddress);
if (group == null)
return null;
var displayName = group.Properties["DisplayName"]?.Value?.ToString();
var alias = group.Properties["Alias"]?.Value?.ToString();
return new cLiamExchangeDistributionGroup(Provider, displayName, primarySmtpAddress, alias);
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamUserInfo>> GetOwnersAsync()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
// Get members of the distribution group
var members = Provider.exchangeManager.GetDistributionGroupMembers(this.DisplayName);
if (members == null)
return new List<cLiamUserInfo>();
var membersList = new List<cLiamUserInfo>();
foreach (var member in members)
{
var userInfo = new cLiamUserInfo
{
DisplayName = member.Properties["DisplayName"]?.Value?.ToString(),
UserPrincipalName = member.Properties["UserPrincipalName"]?.Value?.ToString(),
EMail = member.Properties["PrimarySmtpAddress"]?.Value?.ToString(),
SID = member.Properties["Sid"]?.Value?.ToString()
};
membersList.Add(userInfo);
}
return membersList;
}
catch (Exception E)
{
LogException(E);
return null;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<cLiamPermissionResult> GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
// For distribution groups, we only support adding members (which is essentially Owner role)
if (Role != eLiamAccessRoles.Owner)
{
LogEntry($"Only Owner role is supported for distribution groups, requested: {Role}", LogLevels.Warning);
return new cLiamPermissionResult { Valid = false };
}
bool success = await Provider.AddMemberToGroup(this.DisplayName, User.UserPrincipalName, false);
return new cLiamPermissionResult
{
Valid = success,
UserReference = User.UserPrincipalName
};
}
catch (Exception E)
{
LogException(E);
return new cLiamPermissionResult { Valid = false };
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<bool> RevokePermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
// For distribution groups, we only support removing members (which is essentially Owner role)
if (Role != eLiamAccessRoles.Owner)
{
LogEntry($"Only Owner role is supported for distribution groups, requested: {Role}", LogLevels.Warning);
return false;
}
return await Provider.RemoveMemberFromGroup(this.DisplayName, User.UserPrincipalName, false);
}
catch (Exception E)
{
LogException(E);
return false;
}
finally
{
LogMethodEnd(CM);
}
}
public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
// Distribution groups don't have children in this context
return new List<cLiamDataAreaBase>();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,671 @@
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Linq;
using System.Reflection;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using System.Threading.Tasks;
namespace C4IT.LIAM
{
/// <summary>
/// Manages Exchange operations using PowerShell remoting
/// </summary>
public class ExchangeManager
{
private readonly string _exchangeUri;
private readonly PSCredential _credential;
private readonly string _organizationalUnit;
/// <summary>
/// Constructor with explicit credentials, Exchange URI and OU path
/// </summary>
/// <param name="exchangeUri">URL of the Exchange PowerShell endpoint (e.g. "http://exchangeserver/PowerShell")</param>
/// <param name="credential">Explicit credentials</param>
/// <param name="organizationalUnit">OU path where objects should be created</param>
public ExchangeManager(string exchangeUri, PSCredential credential, string organizationalUnit)
{
_exchangeUri = exchangeUri;
_credential = credential;
_organizationalUnit = organizationalUnit;
}
/// <summary>
/// Creates a runspace connected to the Exchange PowerShell endpoint
/// </summary>
private Runspace CreateRunspace()
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
var connectionInfo = new WSManConnectionInfo(
new Uri(_exchangeUri),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
_credential);
// Set authentication mechanism to Kerberos by default
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
var runspace = RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();
return runspace;
}
catch (Exception ex)
{
LogException(ex);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Creates a distribution group (mailing list). Optionally members can be added initially.
/// </summary>
/// <param name="name">Name of the group</param>
/// <param name="alias">Alias of the group</param>
/// <param name="initialMembers">List of members to be added to the group (optional)</param>
public void CreateDistributionGroup(string name, string alias, List<string> initialMembers = null)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
// Create the distribution group
ps.AddCommand("New-DistributionGroup")
.AddParameter("Name", name)
.AddParameter("Alias", alias)
.AddParameter("OrganizationalUnit", _organizationalUnit);
ps.Invoke();
// Add initial members if specified
if (initialMembers != null)
{
foreach (var member in initialMembers)
{
ps.Commands.Clear();
ps.AddCommand("Add-DistributionGroupMember")
.AddParameter("Identity", name)
.AddParameter("Member", member);
ps.Invoke();
}
}
}
}
catch (Exception ex)
{
LogException(ex);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Creates a security group in Active Directory that can be used for Exchange permissions
/// </summary>
/// <param name="name">Name of the security group</param>
/// <param name="description">Description of the security group</param>
/// <returns>True if successful</returns>
public bool CreateSecurityGroup(string name, string description)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
// Create the security group using New-ADGroup cmdlet
ps.AddCommand("New-ADGroup")
.AddParameter("Name", name)
.AddParameter("GroupScope", "Universal")
.AddParameter("GroupCategory", "Security")
.AddParameter("DisplayName", name)
.AddParameter("Path", _organizationalUnit)
.AddParameter("Description", description);
var result = ps.Invoke();
return result.Count > 0;
}
}
catch (Exception ex)
{
LogException(ex);
return false;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Gets all shared mailboxes matching an optional filter
/// </summary>
/// <param name="filter">Optional filter string</param>
/// <returns>Collection of shared mailboxes as PSObjects</returns>
public IEnumerable<PSObject> GetSharedMailboxes(string filter = null)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-Mailbox")
.AddParameter("RecipientTypeDetails", "SharedMailbox");
if (!string.IsNullOrEmpty(filter))
{
ps.AddParameter("Filter", filter);
}
return ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
return Enumerable.Empty<PSObject>();
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Gets a shared mailbox by primary SMTP address
/// </summary>
/// <param name="primarySmtpAddress">The primary SMTP address of the mailbox</param>
/// <returns>The mailbox as a PSObject, or null if not found</returns>
public PSObject GetSharedMailboxByAddress(string primarySmtpAddress)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-Mailbox")
.AddParameter("RecipientTypeDetails", "SharedMailbox")
.AddParameter("PrimarySmtpAddress", primarySmtpAddress);
var results = ps.Invoke();
return results.Count > 0 ? results[0] : null;
}
}
catch (Exception ex)
{
LogException(ex);
return null;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Gets all distribution groups matching an optional filter
/// </summary>
/// <param name="filter">Optional filter string</param>
/// <returns>Collection of distribution groups as PSObjects</returns>
public IEnumerable<PSObject> GetDistributionGroups(string filter = null)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-DistributionGroup");
if (!string.IsNullOrEmpty(filter))
{
ps.AddParameter("Filter", filter);
}
return ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
return Enumerable.Empty<PSObject>();
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Gets a distribution group by primary SMTP address
/// </summary>
/// <param name="primarySmtpAddress">The primary SMTP address of the group</param>
/// <returns>The distribution group as a PSObject, or null if not found</returns>
public PSObject GetDistributionGroupByAddress(string primarySmtpAddress)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-DistributionGroup")
.AddParameter("PrimarySmtpAddress", primarySmtpAddress);
var results = ps.Invoke();
return results.Count > 0 ? results[0] : null;
}
}
catch (Exception ex)
{
LogException(ex);
return null;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Gets members of a distribution group
/// </summary>
/// <param name="groupName">The name of the distribution group</param>
/// <returns>Collection of members as PSObjects</returns>
public IEnumerable<PSObject> GetDistributionGroupMembers(string groupName)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-DistributionGroupMember")
.AddParameter("Identity", groupName);
return ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
return Enumerable.Empty<PSObject>();
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Gets users who have permissions on a mailbox
/// </summary>
/// <param name="mailboxName">The name of the mailbox</param>
/// <returns>Collection of users as PSObjects</returns>
public IEnumerable<PSObject> GetMailboxPermissionMembers(string mailboxName)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-MailboxPermission")
.AddParameter("Identity", mailboxName)
.AddParameter("AccessRights", "FullAccess");
// Filter out default permissions like NT AUTHORITY\SELF
var results = ps.Invoke().Where(p =>
!p.Properties["User"].Value.ToString().Contains("NT AUTHORITY") &&
!p.Properties["User"].Value.ToString().Contains("Administrator"));
// Now, get the full user objects for each permission
var userList = new List<PSObject>();
using (var ps2 = PowerShell.Create())
{
ps2.Runspace = runspace;
foreach (var perm in results)
{
ps2.Commands.Clear();
ps2.AddCommand("Get-User")
.AddParameter("Identity", perm.Properties["User"].Value.ToString());
var users = ps2.Invoke();
if (users.Count > 0)
{
userList.Add(users[0]);
}
}
}
return userList;
}
}
catch (Exception ex)
{
LogException(ex);
return Enumerable.Empty<PSObject>();
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Gets security groups matching a filter
/// </summary>
/// <param name="filter">LDAP filter to match groups</param>
/// <returns>Collection of security groups as PSObjects</returns>
public IEnumerable<PSObject> GetSecurityGroups(string filter)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-ADGroup")
.AddParameter("Filter", filter)
.AddParameter("Properties", new string[] { "DisplayName", "DistinguishedName", "Sid" });
return ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
return Enumerable.Empty<PSObject>();
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Adds a member to a distribution group
/// </summary>
/// <param name="groupName">Name of the distribution group</param>
/// <param name="member">User to be added as a member</param>
public void AddMemberToDistributionGroup(string groupName, string member)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Add-DistributionGroupMember")
.AddParameter("Identity", groupName)
.AddParameter("Member", member);
ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Removes a member from a distribution group
/// </summary>
/// <param name="groupName">Name of the distribution group</param>
/// <param name="member">User to be removed</param>
public void RemoveMemberFromDistributionGroup(string groupName, string member)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Remove-DistributionGroupMember")
.AddParameter("Identity", groupName)
.AddParameter("Member", member)
.AddParameter("Confirm", false);
ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Adds mailbox permission for a user
/// </summary>
/// <param name="mailboxName">Name of the mailbox</param>
/// <param name="user">User or group to be granted permission</param>
/// <param name="accessRight">Access right (default: FullAccess)</param>
public void AddMailboxPermission(string mailboxName, string user, string accessRight = "FullAccess")
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Add-MailboxPermission")
.AddParameter("Identity", mailboxName)
.AddParameter("User", user)
.AddParameter("AccessRights", accessRight)
.AddParameter("InheritanceType", "All");
ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Removes mailbox permission for a user
/// </summary>
/// <param name="mailboxName">Name of the mailbox</param>
/// <param name="user">User or group to have permission removed</param>
/// <param name="accessRight">Access right (default: FullAccess)</param>
public void RemoveMailboxPermission(string mailboxName, string user, string accessRight = "FullAccess")
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Remove-MailboxPermission")
.AddParameter("Identity", mailboxName)
.AddParameter("User", user)
.AddParameter("AccessRights", accessRight)
.AddParameter("InheritanceType", "All")
.AddParameter("Confirm", false);
ps.Invoke();
}
}
catch (Exception ex)
{
LogException(ex);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Adds Send As permission for a mailbox
/// </summary>
/// <param name="mailboxName">Name of the mailbox</param>
/// <param name="user">User or group to be granted permission</param>
/// <returns>True if successful</returns>
public async Task<bool> AddSendAsPermission(string mailboxName, string user)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Add-ADPermission")
.AddParameter("Identity", mailboxName)
.AddParameter("User", user)
.AddParameter("ExtendedRights", "Send-As")
.AddParameter("AccessRights", "ExtendedRight");
var results = ps.Invoke();
return results.Count > 0;
}
}
catch (Exception ex)
{
LogException(ex);
return false;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Removes Send As permission for a mailbox
/// </summary>
/// <param name="mailboxName">Name of the mailbox</param>
/// <param name="user">User or group to have permission removed</param>
/// <returns>True if successful</returns>
public async Task<bool> RemoveSendAsPermission(string mailboxName, string user)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Remove-ADPermission")
.AddParameter("Identity", mailboxName)
.AddParameter("User", user)
.AddParameter("ExtendedRights", "Send-As")
.AddParameter("AccessRights", "ExtendedRight")
.AddParameter("Confirm", false);
var results = ps.Invoke();
return results.Count > 0;
}
}
catch (Exception ex)
{
LogException(ex);
return false;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
/// <summary>
/// Creates a shared mailbox. Optionally initial permissions (e.g. FullAccess) can be set for members.
/// </summary>
/// <param name="name">Name of the mailbox</param>
/// <param name="alias">Alias of the mailbox</param>
/// <param name="initialMembers">List of users to be added as access rights (optional)</param>
public void CreateSharedMailbox(string name, string alias, List<string> initialMembers = null)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
// Create new shared mailbox
ps.AddCommand("New-Mailbox")
.AddParameter("Name", name)
.AddParameter("Alias", alias)
.AddParameter("Shared", true)
.AddParameter("OrganizationalUnit", _organizationalUnit);
ps.Invoke();
// Add initial members if specified
if (initialMembers != null)
{
foreach (var member in initialMembers)
{
ps.Commands.Clear();
ps.AddCommand("Add-MailboxPermission")
.AddParameter("Identity", name)
.AddParameter("User", member)
.AddParameter("AccessRights", "FullAccess")
.AddParameter("InheritanceType", "All");
ps.Invoke();
}
}
}
}
catch (Exception ex)
{
LogException(ex);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
}
}

View File

@@ -0,0 +1,380 @@
using System;
using System.Linq;
using System.Management.Automation;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.DirectoryServices;
using System.Diagnostics;
using System.Threading;
using System.Management.Automation.Runspaces;
using System.Security.Principal;
namespace C4IT.LIAM
{
public partial class ExchangeManager
{
/// <summary>
/// Stellt sicher, dass eine AD-Sicherheitsgruppe für den angegebenen AccessRole existiert (erstellt sie falls nicht)
/// und wartet optional, bis die Replikation abgeschlossen ist.
/// Liefert den tatsächlichen Gruppennamen zurück.
/// </summary>
private string EnsureSecurityGroup(eLiamAccessRoles accessRole, string baseName)
{
const int MaxLoop = 50; // Abbruchbedingung: nach 50 Versuchen abbrechen
// 1. Namenskonvention für diese Rolle finden
var namingConvention = _provider.NamingConventions
.FirstOrDefault(nc => nc.AccessRole == accessRole);
if (namingConvention == null)
throw new InvalidOperationException($"Keine Namenskonvention für Rolle '{accessRole}' gefunden.");
// 2. Benötigte CustomTags aus dem Provider ziehen
// - Prefix (z.B. "ACL")
// - GROUPTYPEPOSTFIX (z.B. "ExchangeMLMember")
_provider.CustomTags.TryGetValue("ADGroupPrefix", out var prefix);
_provider.CustomTags.TryGetValue(accessRole.ToString(), out var typePostfix);
// 3. Schleife für _LOOP hochzählen, bis ein einzigartiger Name gefunden ist
string groupName = null;
string description = null;
for (int loop = 0; loop <= MaxLoop; loop++)
{
// nur einfügen, wenn loop > 0
var loopPart = loop > 0 ? $"_{loop}" : string.Empty;
// Platzhalter im Template ersetzen
groupName = namingConvention.NamingTemplate
.Replace("{{ADGroupPrefix}}", prefix ?? string.Empty)
.Replace("{{NAME}}", baseName)
.Replace("{{_LOOP}}", loopPart)
.Replace("{{GROUPTYPEPOSTFIX}}", typePostfix ?? string.Empty);
description = namingConvention.DescriptionTemplate
.Replace("{{ADGroupPrefix}}", prefix ?? string.Empty)
.Replace("{{NAME}}", baseName)
.Replace("{{_LOOP}}", loopPart)
.Replace("{{GROUPTYPEPOSTFIX}}", typePostfix ?? string.Empty);
// Existenz prüfen
bool exists = GetSecurityGroups(groupName)
.Any(g => string.Equals(
g.Properties["sAMAccountName"]?.Value?.ToString(),
groupName,
StringComparison.OrdinalIgnoreCase));
if (!exists)
break; // Name ist frei raus aus der Schleife
if (loop == MaxLoop)
throw new InvalidOperationException(
$"Konnte nach {MaxLoop} Versuchen keinen eindeutigen Gruppennamen für '{baseName}' erzeugen.");
}
// 4. Gruppen-Scope-Bit setzen
int scopeBit;
switch (namingConvention.Scope)
{
case eLiamAccessRoleScopes.Global:
scopeBit = 0x2;
break;
case eLiamAccessRoleScopes.DomainLocal:
scopeBit = 0x4;
break;
case eLiamAccessRoleScopes.Universal:
scopeBit = 0x8;
break;
default:
scopeBit = 0x8;
break;
}
int groupType = unchecked((int)(0x80000000 | scopeBit));
// 5. Gruppe im AD anlegen
string ldapPath = $"LDAP://{_organizationalUnit}";
string password = new System.Net.NetworkCredential(string.Empty, _credential.Password).Password;
using (var root = new DirectoryEntry(
ldapPath,
_credential.UserName,
password,
AuthenticationTypes.Secure))
{
var newGroup = root.Children.Add($"CN={groupName}", "group");
newGroup.Properties["sAMAccountName"].Value = groupName;
newGroup.Properties["displayName"].Value = groupName;
newGroup.Properties["groupType"].Value = groupType;
if(!string.IsNullOrEmpty(description))
{
newGroup.Properties["description"].Value = description;
}
newGroup.CommitChanges();
}
// 6. Auf Replikation warten (optional)
const int replicationTimeoutMinutes = 2;
if (!WaitForGroupReplication(groupName, TimeSpan.FromMinutes(replicationTimeoutMinutes)))
{
throw new TimeoutException(
$"Die AD-Gruppe '{groupName}' konnte innerhalb von {replicationTimeoutMinutes} Minuten nicht repliziert werden.");
}
return groupName;
}
/// <summary>
/// Wartet darauf, dass die Gruppe nach der Erstellung im AD repliziert ist.
/// </summary>
private bool WaitForGroupReplication(string groupName, TimeSpan timeout)
{
var sw = Stopwatch.StartNew();
var pollInterval = TimeSpan.FromSeconds(5);
while (sw.Elapsed < timeout)
{
var found = GetSecurityGroups(groupName)
.Any(g => string.Equals(
g.Properties["sAMAccountName"]?.Value?.ToString(),
groupName,
StringComparison.OrdinalIgnoreCase));
if (found) return true;
Thread.Sleep(pollInterval);
}
return false;
}
/// <summary>
/// Setzt das ManagedBy-Attribut einer AD-Gruppe auf eine andere Gruppe
/// mit den im Konstruktor übergebenen Credentials und Domain.
/// </summary>
private void SetManagedBy(string groupName, string managerGroup)
{
string ldapPath = $"LDAP://{_organizationalUnit}";
// SecureString -> Klartext
string password = SecureStringToString(_credential.Password);
using (var root = new DirectoryEntry(ldapPath,
_credential.UserName,
password,
AuthenticationTypes.Secure))
using (var ds = new DirectorySearcher(root))
{
// Gruppe holen
ds.Filter = $"(&(objectClass=group)(sAMAccountName={groupName}))";
var result = ds.FindOne();
if (result == null)
throw new InvalidOperationException($"Gruppe '{groupName}' nicht gefunden in {ldapPath}");
var groupEntry = result.GetDirectoryEntry();
// DistinguishedName der Manager-Gruppe ermitteln
using (var mgrSearch = new DirectorySearcher(root))
{
mgrSearch.Filter = $"(&(objectClass=group)(sAMAccountName={managerGroup}))";
var mgrResult = mgrSearch.FindOne();
if (mgrResult == null)
throw new InvalidOperationException($"Manager-Gruppe '{managerGroup}' nicht gefunden in {ldapPath}");
string managerDn = mgrResult.GetDirectoryEntry()
.Properties["distinguishedName"]
.Value
.ToString();
// Attribut setzen und speichern
groupEntry.Properties["managedBy"].Value = managerDn;
groupEntry.CommitChanges();
}
}
}
/// <summary>
/// Erstellt eine Shared Mailbox samt zugehöriger AD-Gruppen (FullAccess, SendAs, Owner) und setzt die nötigen Berechtigungen.
/// </summary>
public Tuple<Guid, List<Tuple<string, string, string, string>>> CreateSharedMailboxWithOwnershipGroups(
string name,
string alias,
string displayName = null,
string primarySmtpAddress = null)
{
CreationResult result = new CreationResult();
// Ensure AD groups
string fullAccessGroup = EnsureSecurityGroup(eLiamAccessRoles.ExchangeSMBFullAccess, name);
string sendAsGroup = EnsureSecurityGroup(eLiamAccessRoles.ExchangeSMBSendAs, name);
string ownerGroup = EnsureSecurityGroup(eLiamAccessRoles.ExchangeSMBOwner, name);
SetManagedBy(fullAccessGroup, ownerGroup);
SetManagedBy(sendAsGroup, ownerGroup);
// Create mailbox
using (Runspace rs = CreateRunspace())
{
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = rs;
ps.AddCommand("New-Mailbox");
ps.AddParameter("Name", name);
ps.AddParameter("Alias", alias);
ps.AddParameter("Shared", true);
ps.AddParameter("OrganizationalUnit", _organizationalUnit);
if (!string.IsNullOrEmpty(displayName))
ps.AddParameter("DisplayName", displayName);
if (!string.IsNullOrEmpty(primarySmtpAddress))
ps.AddParameter("PrimarySmtpAddress", primarySmtpAddress);
ps.Invoke();
AddMailboxPermission(name, fullAccessGroup, "FullAccess");
AddSendAsPermission(name, sendAsGroup).GetAwaiter().GetResult();
}
}
// Retrieve mailbox GUID
DirectoryEntry mbEntry = FindAdObject("(&(objectClass=user)(mailNickname=" + alias + "))");
if (mbEntry != null && mbEntry.Properties.Contains("objectGUID") && mbEntry.Properties["objectGUID"].Count > 0)
{
byte[] bytes = (byte[])mbEntry.Properties["objectGUID"][0];
result.ObjectGuid = new Guid(bytes);
}
// Collect group details
string[] roles = new string[] {
eLiamAccessRoles.ExchangeSMBFullAccess.ToString(),
eLiamAccessRoles.ExchangeSMBSendAs.ToString(),
eLiamAccessRoles.ExchangeSMBOwner.ToString()
};
string[] names = new string[] {
fullAccessGroup,
sendAsGroup,
ownerGroup
};
for (int i = 0; i < roles.Length; i++)
{
DirectoryEntry grpEntry = FindAdObject("(&(objectCategory=group)(sAMAccountName=" + names[i] + "))");
if (grpEntry != null && grpEntry.Properties.Contains("objectSid") && grpEntry.Properties["objectSid"].Count > 0)
{
byte[] sidBytes = (byte[])grpEntry.Properties["objectSid"][0];
string sid = new SecurityIdentifier(sidBytes, 0).Value;
string distinguishedName = grpEntry.Properties["distinguishedName"][0].ToString();
result.Groups.Add(Tuple.Create(roles[i], sid, names[i], distinguishedName));
}
}
return Tuple.Create(result.ObjectGuid, result.Groups);
}
/// <summary>
/// Erstellt eine Distribution Group samt zugehöriger AD-Gruppen (Member, Owner) und setzt die nötigen Berechtigungen.
/// </summary>
public Tuple<Guid, List<Tuple<string, string, string, string>>> CreateDistributionGroupWithOwnershipGroups(
string name,
string alias,
string displayName = null,
string primarySmtpAddress = null)
{
CreationResult result = new CreationResult();
// Ensure AD groups
string memberGroup = EnsureSecurityGroup(eLiamAccessRoles.ExchangeMLMember, name);
string ownerGroup = EnsureSecurityGroup(eLiamAccessRoles.ExchangeMLOwner, name);
SetManagedBy(memberGroup, ownerGroup);
// Create distribution group
using (Runspace rs = CreateRunspace())
{
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = rs;
ps.AddCommand("New-DistributionGroup");
ps.AddParameter("Name", name);
ps.AddParameter("Alias", alias);
ps.AddParameter("OrganizationalUnit", _organizationalUnit);
if (!string.IsNullOrEmpty(displayName))
ps.AddParameter("DisplayName", displayName);
if (!string.IsNullOrEmpty(primarySmtpAddress))
ps.AddParameter("PrimarySmtpAddress", primarySmtpAddress);
ps.Invoke();
// b) GUID holen
ps.Commands.Clear();
ps.AddCommand("Get-DistributionGroup")
.AddParameter("Identity", name);
var dg = ps.Invoke().FirstOrDefault();
if (dg != null && dg.Properties["Guid"] != null)
{
var guidVal = dg.Properties["Guid"].Value;
if (guidVal is Guid g) result.ObjectGuid = g;
else if (guidVal is string s && Guid.TryParse(s, out Guid parsed)) result.ObjectGuid = parsed;
}
AddMemberToDistributionGroup(name, memberGroup);
SetDistributionGroupManagedBy(name, ownerGroup);
}
}
// Collect group details
string[] dRoles = new string[] {
eLiamAccessRoles.ExchangeMLMember.ToString(),
eLiamAccessRoles.ExchangeMLOwner.ToString()
};
string[] dNames = new string[] {
memberGroup,
ownerGroup
};
for (int i = 0; i < dRoles.Length; i++)
{
DirectoryEntry grpEntry = FindAdObject("(&(objectCategory=group)(sAMAccountName=" + dNames[i] + "))");
if (grpEntry != null && grpEntry.Properties.Contains("objectSid") && grpEntry.Properties["objectSid"].Count > 0)
{
byte[] sidBytes = (byte[])grpEntry.Properties["objectSid"][0];
string sid = new SecurityIdentifier(sidBytes, 0).Value;
string distinguishedName = grpEntry.Properties["distinguishedName"][0].ToString();
result.Groups.Add(Tuple.Create(dRoles[i], sid, dNames[i], distinguishedName));
}
}
return Tuple.Create(result.ObjectGuid, result.Groups);
}
/// <summary>
/// Setzt das ManagedBy-Attribut einer Distribution Group.
/// </summary>
private void SetDistributionGroupManagedBy(string groupName, string managerGroup)
{
using (var runspace = CreateRunspace())
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Set-DistributionGroup")
.AddParameter("Identity", groupName)
.AddParameter("ManagedBy", managerGroup)
.AddParameter("ErrorAction", "SilentlyContinue");
ps.Invoke();
}
}
/// <summary>
/// Hilfsmethode: SecureString in Klartext wandeln.
/// </summary>
private static string SecureStringToString(SecureString ss)
{
if (ss == null) return string.Empty;
IntPtr ptr = Marshal.SecureStringToBSTR(ss);
try
{
return Marshal.PtrToStringBSTR(ptr) ?? string.Empty;
}
finally
{
Marshal.ZeroFreeBSTR(ptr);
}
}
}
}

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{12586A29-BB1E-49B4-B971-88E520D6A77C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LiamExchange</RootNamespace>
<AssemblyName>LiamExchange</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Bcl.Cryptography, Version=9.0.0.4, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.Cryptography.9.0.4\lib\net462\Microsoft.Bcl.Cryptography.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.Formats.Asn1, Version=9.0.0.4, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Formats.Asn1.9.0.4\lib\net462\System.Formats.Asn1.dll</HintPath>
</Reference>
<Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\lib\net4\System.Management.Automation.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="C4IT.LIAM.Exchange.cs" />
<Compile Include="ExchangeManager.Extensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LiamBaseClasses\LiamBaseClasses.csproj">
<Project>{3531c9e6-cf6e-458e-b604-4a5a8d1c7ab0}</Project>
<Name>LiamBaseClasses</Name>
</ProjectReference>
<ProjectReference Include="..\LiamHelper\LiamHelper.csproj">
<Project>{6b0e73a6-f918-42d5-9525-d59d4d16283d}</Project>
<Name>LiamHelper</Name>
</ProjectReference>
<ProjectReference Include="..\LiamNtfs\LiamNtfs.csproj">
<Project>{7F3085F7-1B7A-4DB2-B66F-1B69CCB0002F}</Project>
<Name>LiamNtfs</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="C4IT.LIAM.ExchangeManager.cs" />
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}

View File

@@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("LiamExchange")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LiamExchange")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("12586a29-bb1e-49b4-b971-88e520d6a77c")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

11
LiamExchange/app.config Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?><doc>
<assembly>
<name>System.Buffers</name>
</assembly>
<members>
<member name="T:System.Buffers.ArrayPool`1">
<summary>Provides a resource pool that enables reusing instances of type <see cref="T[]"></see>.</summary>
<typeparam name="T">The type of the objects that are in the resource pool.</typeparam>
</member>
<member name="M:System.Buffers.ArrayPool`1.#ctor">
<summary>Initializes a new instance of the <see cref="T:System.Buffers.ArrayPool`1"></see> class.</summary>
</member>
<member name="M:System.Buffers.ArrayPool`1.Create">
<summary>Creates a new instance of the <see cref="T:System.Buffers.ArrayPool`1"></see> class.</summary>
<returns>A new instance of the <see cref="System.Buffers.ArrayPool`1"></see> class.</returns>
</member>
<member name="M:System.Buffers.ArrayPool`1.Create(System.Int32,System.Int32)">
<summary>Creates a new instance of the <see cref="T:System.Buffers.ArrayPool`1"></see> class using the specifed configuration.</summary>
<param name="maxArrayLength">The maximum length of an array instance that may be stored in the pool.</param>
<param name="maxArraysPerBucket">The maximum number of array instances that may be stored in each bucket in the pool. The pool groups arrays of similar lengths into buckets for faster access.</param>
<returns>A new instance of the <see cref="System.Buffers.ArrayPool`1"></see> class with the specified configuration.</returns>
</member>
<member name="M:System.Buffers.ArrayPool`1.Rent(System.Int32)">
<summary>Retrieves a buffer that is at least the requested length.</summary>
<param name="minimumLength">The minimum length of the array.</param>
<returns>An array of type <see cref="T[]"></see> that is at least <paramref name="minimumLength">minimumLength</paramref> in length.</returns>
</member>
<member name="M:System.Buffers.ArrayPool`1.Return(`0[],System.Boolean)">
<summary>Returns an array to the pool that was previously obtained using the <see cref="M:System.Buffers.ArrayPool`1.Rent(System.Int32)"></see> method on the same <see cref="T:System.Buffers.ArrayPool`1"></see> instance.</summary>
<param name="array">A buffer to return to the pool that was previously obtained using the <see cref="M:System.Buffers.ArrayPool`1.Rent(System.Int32)"></see> method.</param>
<param name="clearArray">Indicates whether the contents of the buffer should be cleared before reuse. If <paramref name="clearArray">clearArray</paramref> is set to true, and if the pool will store the buffer to enable subsequent reuse, the <see cref="M:System.Buffers.ArrayPool`1.Return(`0[],System.Boolean)"></see> method will clear the <paramref name="array">array</paramref> of its contents so that a subsequent caller using the <see cref="M:System.Buffers.ArrayPool`1.Rent(System.Int32)"></see> method will not see the content of the previous caller. If <paramref name="clearArray">clearArray</paramref> is set to false or if the pool will release the buffer, the array&amp;#39;s contents are left unchanged.</param>
</member>
<member name="P:System.Buffers.ArrayPool`1.Shared">
<summary>Gets a shared <see cref="T:System.Buffers.ArrayPool`1"></see> instance.</summary>
<returns>A shared <see cref="System.Buffers.ArrayPool`1"></see> instance.</returns>
</member>
</members>
</doc>

Some files were not shown because too many files have changed in this diff Show More