using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Security; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using C4IT.Logging; using C4IT.Matrix42.ServerInfo; using static C4IT.Logging.cLogManager; namespace C4IT.LIAM { public static class LiamExchangeProviderRegistry { /// /// This registry helps us manage Exchange providers since they can't be registered in the enum /// private static Dictionary _registeredProviders = new Dictionary(); /// /// Register an Exchange provider /// public static void RegisterProvider(Guid configId, cLiamProviderExchange provider) { if (_registeredProviders.ContainsKey(configId)) { _registeredProviders[configId] = provider; } else { _registeredProviders.Add(configId, provider); } } /// /// Get a registered Exchange provider /// public static cLiamProviderExchange GetProvider(Guid configId) { if (_registeredProviders.ContainsKey(configId)) { return _registeredProviders[configId]; } return null; } } public static class LiamInitializer { public static Guid exchangeModuleId = new Guid("f8a23b4c-2d92-ec11-5437-00155d500102"); static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) { return new cLiamProviderExchange(LiamConfiguration, ProviderData); } } public class cLiamProviderExchange : cLiamProviderBase { private ExchangeManager _exchangeManager = null; private bool _isOnline = false; public string LastErrorMessage { get; private set; } = null; public Exception LastException { get; private set; } = null; public cLiamProviderExchange(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) : base(LiamConfiguration, ProviderData) { } public override async Task LogonAsync() { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.exchangeModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return false; } return await LogonAsync(true); } public async Task LogonAsync(bool force = false) { if (!force && _isOnline) return true; var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (_exchangeManager != null) { _exchangeManager.Dispose(); _exchangeManager = null; } SecureString securePassword = new SecureString(); if (this.Credential?.Secret != null) { foreach (char c in this.Credential.Secret) { securePassword.AppendChar(c); } securePassword.MakeReadOnly(); } _exchangeManager = new ExchangeManager( this.RootPath, // Exchange Server FQDN this.Credential?.Identification, securePassword ); _isOnline = true; return true; } catch (Exception E) { LogException(E); LastException = E; LastErrorMessage = $"Exchange login failed: {E.Message}"; _isOnline = false; return false; } finally { LogMethodEnd(CM); } } public override string GetLastErrorMessage() { return LastErrorMessage; } public override async Task> getDataAreasAsync(int Depth = -1) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.exchangeModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return new List(); } if (!await LogonAsync()) return null; var DataAreas = new List(); // Get Shared Mailboxes var sharedMailboxes = _exchangeManager.GetSharedMailboxes(); foreach (var mailbox in sharedMailboxes) { string displayName = mailbox.Properties["DisplayName"]?.Value?.ToString() ?? "N/A"; string email = mailbox.Properties["PrimarySmtpAddress"]?.Value?.ToString() ?? "N/A"; string alias = mailbox.Properties["Alias"]?.Value?.ToString() ?? "N/A"; if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(displayName, this.DataAreaRegEx).Success) continue; var sharedMailbox = new cLiamExchangeSharedMailbox(this, new cExchangeResultSharedMailbox { DisplayName = displayName, EmailAddress = email, Alias = alias, PSObject = mailbox }); DataAreas.Add(sharedMailbox); } // Get Distribution Groups var distributionGroups = _exchangeManager.GetDistributionGroups(); foreach (var group in distributionGroups) { string displayName = group.Properties["DisplayName"]?.Value?.ToString() ?? "N/A"; string email = group.Properties["PrimarySmtpAddress"]?.Value?.ToString() ?? "N/A"; string alias = group.Properties["Alias"]?.Value?.ToString() ?? "N/A"; if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(displayName, this.DataAreaRegEx).Success) continue; var distributionGroup = new cLiamExchangeDistributionGroup(this, new cExchangeResultDistributionGroup { DisplayName = displayName, EmailAddress = email, Alias = alias, PSObject = group }); DataAreas.Add(distributionGroup); } return DataAreas; } catch (Exception E) { LogException(E); LastException = E; LastErrorMessage = $"Failed to get Exchange data areas: {E.Message}"; return null; } finally { LogMethodEnd(CM); } } public override async Task LoadDataArea(string UID) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(LiamInitializer.exchangeModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return null; } if (!await LogonAsync()) return null; // Parse the UID to determine the type and ID var parts = UID.Split('|'); if (parts.Length < 2) return null; string type = parts[0]; string id = parts[1]; if (type.Equals("SharedMailbox", StringComparison.OrdinalIgnoreCase)) { // Implement loading a specific shared mailbox // This would require a command like Get-Mailbox -Identity return null; // Placeholder, implement actual loading } else if (type.Equals("DistributionGroup", StringComparison.OrdinalIgnoreCase)) { // Implement loading a specific distribution group // This would require a command like Get-DistributionGroup -Identity return null; // Placeholder, implement actual loading } return null; } catch (Exception E) { LogException(E); LastException = E; LastErrorMessage = $"Failed to load Exchange data area: {E.Message}"; return null; } finally { LogMethodEnd(CM); } } public override async Task> getSecurityGroupsAsync(string groupFilter) { await Task.Delay(0); // Just to make the method async return new List(); // Not implemented for Exchange provider } // Method to create a new shared mailbox public async Task CreateSharedMailbox(string name, string alias, string userPrincipalName, string organizationalUnit = null) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!await LogonAsync()) return false; _exchangeManager.CreateSharedMailbox(name, alias, userPrincipalName, organizationalUnit); return true; } catch (Exception E) { LogException(E); LastException = E; LastErrorMessage = $"Failed to create shared mailbox: {E.Message}"; return false; } finally { LogMethodEnd(CM); } } // Method to create a new distribution group public async Task CreateDistributionGroup(string name, string alias, string organizationalUnit = null, string managedBy = null) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!await LogonAsync()) return false; _exchangeManager.CreateDistributionGroup(name, alias, organizationalUnit, managedBy); return true; } catch (Exception E) { LogException(E); LastException = E; LastErrorMessage = $"Failed to create distribution group: {E.Message}"; return false; } finally { LogMethodEnd(CM); } } // Method to add member to distribution group public async Task AddMemberToDistributionGroup(string groupIdentity, string memberIdentity) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!await LogonAsync()) return false; _exchangeManager.AddMemberToDistributionGroup(groupIdentity, memberIdentity); return true; } catch (Exception E) { LogException(E); LastException = E; LastErrorMessage = $"Failed to add member to distribution group: {E.Message}"; return false; } finally { LogMethodEnd(CM); } } // Method to add full access permission to a shared mailbox public async Task AddFullAccessPermission(string mailboxIdentity, string userIdentity) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!await LogonAsync()) return false; _exchangeManager.AddFullAccessPermission(mailboxIdentity, userIdentity); return true; } catch (Exception E) { LogException(E); LastException = E; LastErrorMessage = $"Failed to add full access permission: {E.Message}"; return false; } finally { LogMethodEnd(CM); } } // Dispose pattern to clean up Exchange manager private bool disposed = false; public void Dispose(bool disposing) { if (!disposed) { if (disposing) { if (_exchangeManager != null) { _exchangeManager.Dispose(); _exchangeManager = null; } } disposed = true; } } ~cLiamProviderExchange() { Dispose(false); } } // Base class for Exchange results public class cExchangeResultBase { public string DisplayName { get; set; } public string EmailAddress { get; set; } public string Alias { get; set; } public PSObject PSObject { get; set; } } // Class for Shared Mailbox results public class cExchangeResultSharedMailbox : cExchangeResultBase { } // Class for Distribution Group results public class cExchangeResultDistributionGroup : cExchangeResultBase { } // Class representing an Exchange Shared Mailbox public class cLiamExchangeSharedMailbox : cLiamDataAreaBase { public new readonly cLiamProviderExchange Provider = null; private readonly cExchangeResultSharedMailbox Mailbox = null; public cLiamExchangeSharedMailbox(cLiamProviderExchange Provider, cExchangeResultSharedMailbox Mailbox) : base(Provider) { this.Provider = Provider; this.Mailbox = Mailbox; this.DisplayName = Mailbox.DisplayName; this.TechnicalName = Mailbox.EmailAddress; this.UID = $"SharedMailbox|{Mailbox.Alias}"; this.Level = 0; this.DataType = eLiamDataAreaTypes.Unknown; // No specific type for Exchange in the enum this.SupportsPermissions = true; } public override async Task> getChildrenAsync(int Depth = -1) { await Task.Delay(0); // Just to make the method async return new List(); // Shared mailboxes don't have children } public override async Task> GetPermissionsAsync(bool force = false) { // Implementation would retrieve permissions for the mailbox await Task.Delay(0); return null; // Placeholder, actual implementation would query Exchange } public override async Task GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role) { var result = new cLiamPermissionResult { Valid = false }; try { // Convert LIAM access role to Exchange permission if (Role == eLiamAccessRoles.Owner || Role == eLiamAccessRoles.Write) { // Add full access permission bool success = await Provider.AddFullAccessPermission(Mailbox.Alias, User.UserPrincipalName); if (success) { result.Valid = true; result.UserReference = User.UserPrincipalName; } } // Could also implement Send-As permissions here } catch (Exception E) { LogException(E); } return result; } } // Class representing an Exchange Distribution Group public class cLiamExchangeDistributionGroup : cLiamDataAreaBase { public new readonly cLiamProviderExchange Provider = null; private readonly cExchangeResultDistributionGroup Group = null; public cLiamExchangeDistributionGroup(cLiamProviderExchange Provider, cExchangeResultDistributionGroup Group) : base(Provider) { this.Provider = Provider; this.Group = Group; this.DisplayName = Group.DisplayName; this.TechnicalName = Group.EmailAddress; this.UID = $"DistributionGroup|{Group.Alias}"; this.Level = 0; this.DataType = eLiamDataAreaTypes.Unknown; // No specific type for Exchange in the enum this.SupportsPermissions = true; } public override async Task> getChildrenAsync(int Depth = -1) { await Task.Delay(0); // Just to make the method async return new List(); // Groups could potentially have members as children } public override async Task> GetPermissionsAsync(bool force = false) { // Implementation would retrieve members of the group await Task.Delay(0); return null; // Placeholder, actual implementation would query Exchange } public override async Task GrantPermissionAsync(cLiamUserInfo User, eLiamAccessRoles Role) { var result = new cLiamPermissionResult { Valid = false }; try { // Add user to the distribution group bool success = await Provider.AddMemberToDistributionGroup(Group.Alias, User.UserPrincipalName); if (success) { result.Valid = true; result.UserReference = User.UserPrincipalName; } } catch (Exception E) { LogException(E); } return result; } } }