initial
This commit is contained in:
535
LieamExchange/C4IT.LIAM.Exchange.cs
Normal file
535
LieamExchange/C4IT.LIAM.Exchange.cs
Normal file
@@ -0,0 +1,535 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// This registry helps us manage Exchange providers since they can't be registered in the enum
|
||||
/// </summary>
|
||||
private static Dictionary<Guid, cLiamProviderExchange> _registeredProviders = new Dictionary<Guid, cLiamProviderExchange>();
|
||||
|
||||
/// <summary>
|
||||
/// Register an Exchange provider
|
||||
/// </summary>
|
||||
public static void RegisterProvider(Guid configId, cLiamProviderExchange provider)
|
||||
{
|
||||
if (_registeredProviders.ContainsKey(configId))
|
||||
{
|
||||
_registeredProviders[configId] = provider;
|
||||
}
|
||||
else
|
||||
{
|
||||
_registeredProviders.Add(configId, provider);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a registered Exchange provider
|
||||
/// </summary>
|
||||
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<bool> 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<bool> 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<List<cLiamDataAreaBase>> 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<cLiamDataAreaBase>();
|
||||
}
|
||||
|
||||
if (!await LogonAsync())
|
||||
return null;
|
||||
|
||||
var DataAreas = new List<cLiamDataAreaBase>();
|
||||
|
||||
// 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<cLiamDataAreaBase> 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 <id>
|
||||
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 <id>
|
||||
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<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
|
||||
{
|
||||
await Task.Delay(0); // Just to make the method async
|
||||
return new List<cLiamDataAreaBase>(); // Not implemented for Exchange provider
|
||||
}
|
||||
|
||||
// Method to create a new shared mailbox
|
||||
public async Task<bool> 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<bool> 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<bool> 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<bool> 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<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
|
||||
{
|
||||
await Task.Delay(0); // Just to make the method async
|
||||
return new List<cLiamDataAreaBase>(); // Shared mailboxes don't have children
|
||||
}
|
||||
|
||||
public override async Task<List<cLiamPermissionInfo>> 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<cLiamPermissionResult> 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<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
|
||||
{
|
||||
await Task.Delay(0); // Just to make the method async
|
||||
return new List<cLiamDataAreaBase>(); // Groups could potentially have members as children
|
||||
}
|
||||
|
||||
public override async Task<List<cLiamPermissionInfo>> 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<cLiamPermissionResult> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user