Files
LIAM/LiamWorkflowActivities/LiamWorkflowRuntime.cs

572 lines
23 KiB
C#

using C4IT.LIAM;
using C4IT.Logging;
using C4IT.MsGraph;
using C4IT_IAM_Engine;
using LiamAD;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;
using static LiamAD.ADServiceGroupCreator;
namespace LiamWorkflowActivities
{
public class GetDataAreasOperationResult
{
public bool Success { get; set; }
public string ErrorCode { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
public List<DataAreaEntry> DataAreas { get; set; } = new List<DataAreaEntry>();
}
public class GetSecurityGroupsOperationResult
{
public bool Success { get; set; }
public string ErrorCode { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
public List<SecurityGroupEntry> SecurityGroups { get; set; } = new List<SecurityGroupEntry>();
}
public class NtfsOperationResult
{
public bool Success { get; set; }
public ResultToken ResultToken { get; set; }
}
public class AdServiceGroupOperationResult
{
public bool Success { get; set; }
public string ErrorCode { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
public List<Tuple<string, string, string, string>> CreatedGroups { get; set; } = new List<Tuple<string, string, string, string>>();
}
public class ExchangeProvisionOperationResult
{
public bool Success { get; set; }
public Guid ObjectGuid { get; set; } = Guid.Empty;
public List<Tuple<string, string, string, string>> CreatedGroups { get; set; } = new List<Tuple<string, string, string, string>>();
public string ErrorCode { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
}
public class CloneTeamOperationResult
{
public bool Success { get; set; }
public Guid CreatedTeamId { get; set; } = Guid.Empty;
public cMsGraphResultBase Result { get; set; }
public string ErrorCode { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
}
public static class LiamWorkflowRuntime
{
public static async Task<GetDataAreasOperationResult> GetDataAreasFromProviderAsync(cLiamProviderBase provider, string configurationId = null)
{
var result = new GetDataAreasOperationResult();
if (provider == null)
{
result.ErrorCode = "WF_GET_DATAAREAS_PROVIDER_NOT_FOUND";
result.ErrorMessage = "Configured provider is not initialized.";
return result;
}
try
{
var dataAreas = await provider.getDataAreasAsync(provider.MaxDepth);
if (dataAreas == null)
{
SetErrorFromProvider(result, provider, "WF_GET_DATAAREAS_PROVIDER_CALL_FAILED", "Provider returned null while reading data areas.");
return result;
}
if (!await EnsureNtfsPermissionGroupsIfConfiguredAsync(provider, dataAreas, result))
return result;
result.DataAreas = dataAreas
.Select(dataArea => MapDataAreaEntry(dataArea, configurationId))
.ToList();
result.Success = true;
return result;
}
catch (Exception ex)
{
LogException(ex);
result.ErrorCode = "WF_GET_DATAAREAS_EXCEPTION";
result.ErrorMessage = ex.Message;
return result;
}
}
public static async Task<GetSecurityGroupsOperationResult> GetSecurityGroupsFromProviderAsync(cLiamProviderBase provider)
{
var result = new GetSecurityGroupsOperationResult();
if (provider == null)
{
result.ErrorCode = "WF_GET_SECURITYGROUPS_PROVIDER_NOT_FOUND";
result.ErrorMessage = "Configured provider is not initialized.";
return result;
}
try
{
var securityGroups = await provider.getSecurityGroupsAsync(provider.GroupFilter);
if (securityGroups == null)
{
SetErrorFromProvider(result, provider, "WF_GET_SECURITYGROUPS_PROVIDER_CALL_FAILED", "Provider returned null while reading security groups.");
return result;
}
result.SecurityGroups = securityGroups
.Select(MapSecurityGroupEntry)
.ToList();
result.Success = true;
return result;
}
catch (Exception ex)
{
LogException(ex);
result.ErrorCode = "WF_GET_SECURITYGROUPS_EXCEPTION";
result.ErrorMessage = ex.Message;
return result;
}
}
public static async Task<NtfsOperationResult> CreateDataAreaAsync(
cLiamProviderBase provider,
string newFolderPath,
string parentFolderPath,
IDictionary<string, string> customTags,
IEnumerable<string> ownerSids,
IEnumerable<string> readerSids,
IEnumerable<string> writerSids)
{
var result = new NtfsOperationResult();
if (!(provider is cLiamProviderNtfs ntfsProvider))
{
result.ResultToken = CreateInvalidNtfsResultToken("Configured provider is not NTFS or not initialized.");
return result;
}
var token = await ntfsProvider.CreateDataAreaAsync(
newFolderPath,
parentFolderPath,
customTags,
NormalizeIdentifierList(ownerSids),
NormalizeIdentifierList(readerSids),
NormalizeIdentifierList(writerSids));
if (token == null)
token = CreateInvalidNtfsResultToken(ntfsProvider.GetLastErrorMessage() ?? "Provider returned no result while creating the data area.");
result.ResultToken = token;
result.Success = token != null && token.resultErrorId == 0;
return result;
}
public static async Task<NtfsOperationResult> EnsureNtfsPermissionGroupsAsync(
cLiamProviderBase provider,
string folderPath,
IDictionary<string, string> customTags,
IEnumerable<string> ownerSids,
IEnumerable<string> readerSids,
IEnumerable<string> writerSids,
bool ensureTraverseGroups)
{
var result = new NtfsOperationResult();
if (!(provider is cLiamProviderNtfs ntfsProvider) || string.IsNullOrWhiteSpace(folderPath))
{
result.ResultToken = CreateInvalidNtfsResultToken(provider is cLiamProviderNtfs
? "Folder path is missing."
: "Configured provider is not NTFS or not initialized.");
return result;
}
var token = await ntfsProvider.EnsureMissingPermissionGroupsAsync(
folderPath,
customTags,
NormalizeIdentifierList(ownerSids),
NormalizeIdentifierList(readerSids),
NormalizeIdentifierList(writerSids),
ensureTraverseGroups);
if (token == null)
token = CreateInvalidNtfsResultToken(ntfsProvider.GetLastErrorMessage() ?? "Provider returned no result while ensuring NTFS permission groups.");
result.ResultToken = token;
result.Success = token != null && token.resultErrorId == 0;
return result;
}
public static AdServiceGroupOperationResult CreateAdServiceGroups(
cLiamProviderBase provider,
string serviceName,
string description,
eLiamAccessRoleScopes scope,
ADGroupType groupType,
IEnumerable<string> ownerSids,
IEnumerable<string> memberSids)
{
var result = new AdServiceGroupOperationResult();
if (!(provider is cLiamProviderAD adProvider))
{
result.ErrorCode = "WF_PROVIDER_INVALID";
result.ErrorMessage = "Configured provider is not Active Directory or not initialized.";
return result;
}
try
{
var groups = adProvider.CreateServiceGroups(
serviceName,
description,
scope,
groupType,
NormalizeIdentifierList(ownerSids),
NormalizeIdentifierList(memberSids));
result.Success = groups != null;
result.CreatedGroups = groups ?? new List<Tuple<string, string, string, string>>();
return result;
}
catch (Exception ex)
{
LogException(ex);
result.ErrorCode = "WF_ACTIVITY_EXCEPTION";
result.ErrorMessage = ex.Message;
return result;
}
}
public static async Task<CloneTeamOperationResult> CloneTeamAsync(
cLiamProviderBase provider,
string teamId,
string name,
string description,
int visibility,
int partsToClone,
string additionalMembers,
string additionalOwners)
{
var result = new CloneTeamOperationResult();
if (!(provider is cLiamProviderMsTeams msTeamsProvider))
{
result.ErrorCode = "WF_PROVIDER_INVALID";
result.ErrorMessage = "Configured provider is not MsTeams or not initialized.";
return result;
}
try
{
var cloneResult = await msTeamsProvider.cloneTeam(teamId, name, description, visibility, partsToClone, additionalMembers, additionalOwners);
result.Result = cloneResult;
result.Success = cloneResult != null;
if (cloneResult?.Result?.targetResourceId != null)
{
string idString = cloneResult.Result.targetResourceId.ToString();
Guid createdTeamId;
if (Guid.TryParse(idString, out createdTeamId))
{
result.CreatedTeamId = createdTeamId;
}
else
{
LogEntry($"targetResourceId '{idString}' is not a valid Guid.", LogLevels.Warning);
}
}
return result;
}
catch (Exception ex)
{
LogException(ex);
result.ErrorCode = "WF_ACTIVITY_EXCEPTION";
result.ErrorMessage = ex.Message;
return result;
}
}
public static ExchangeProvisionOperationResult CreateDistributionGroup(
cLiamProviderBase provider,
string name,
string alias,
string displayName,
string primarySmtpAddress)
{
var result = new ExchangeProvisionOperationResult();
if (!(provider is cLiamProviderExchange exchangeProvider))
{
result.ErrorCode = "WF_PROVIDER_INVALID";
result.ErrorMessage = "Configured provider is not Exchange or not initialized.";
return result;
}
try
{
var created = exchangeProvider.exchangeManager.CreateDistributionGroupWithOwnershipGroups(
name,
alias,
displayName,
primarySmtpAddress,
out string errorCode,
out string errorMessage);
result.ErrorCode = errorCode ?? string.Empty;
result.ErrorMessage = errorMessage ?? string.Empty;
if (created != null)
{
result.Success = true;
result.ObjectGuid = created.Item1;
result.CreatedGroups = created.Item2 ?? new List<Tuple<string, string, string, string>>();
}
return result;
}
catch (Exception ex)
{
LogException(ex);
result.ErrorCode = "WF_ACTIVITY_EXCEPTION";
result.ErrorMessage = ex.Message;
return result;
}
}
public static ExchangeProvisionOperationResult CreateSharedMailbox(
cLiamProviderBase provider,
string name,
string alias,
string displayName,
string primarySmtpAddress)
{
var result = new ExchangeProvisionOperationResult();
if (!(provider is cLiamProviderExchange exchangeProvider))
{
result.ErrorCode = "WF_PROVIDER_INVALID";
result.ErrorMessage = "Configured provider is not Exchange or not initialized.";
return result;
}
try
{
var created = exchangeProvider.exchangeManager.CreateSharedMailboxWithOwnershipGroups(
name,
alias,
displayName,
primarySmtpAddress,
out string errorCode,
out string errorMessage);
result.ErrorCode = errorCode ?? string.Empty;
result.ErrorMessage = errorMessage ?? string.Empty;
if (created != null)
{
result.Success = true;
result.ObjectGuid = created.Item1;
result.CreatedGroups = created.Item2 ?? new List<Tuple<string, string, string, string>>();
}
return result;
}
catch (Exception ex)
{
LogException(ex);
result.ErrorCode = "WF_ACTIVITY_EXCEPTION";
result.ErrorMessage = ex.Message;
return result;
}
}
private static ResultToken CreateInvalidNtfsResultToken(string message)
{
return new ResultToken("LiamWorkflowRuntime")
{
resultErrorId = 1,
resultMessage = message ?? string.Empty
};
}
private static IEnumerable<string> NormalizeIdentifierList(IEnumerable<string> identifiers)
{
if (identifiers == null)
return Enumerable.Empty<string>();
return identifiers
.Select(i => i?.Trim())
.Where(i => !string.IsNullOrWhiteSpace(i))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
}
private static async Task<bool> EnsureNtfsPermissionGroupsIfConfiguredAsync(cLiamProviderBase provider, List<cLiamDataAreaBase> dataAreas, GetDataAreasOperationResult result)
{
if (!(provider is cLiamProviderNtfs ntfsProvider))
return true;
if (!IsAdditionalConfigurationEnabled(provider, "EnsureNtfsPermissionGroups"))
return true;
foreach (var ntfsArea in dataAreas.OfType<cLiamNtfsFolder>())
{
var folderPath = ntfsArea.TechnicalName;
if (string.IsNullOrWhiteSpace(folderPath))
continue;
if (!Directory.Exists(folderPath))
{
LogEntry($"Skipping automatic NTFS permission group ensure for '{folderPath}' because the directory does not exist.", LogLevels.Warning);
continue;
}
var ensureResult = await ntfsProvider.EnsureMissingPermissionGroupsAsync(
folderPath,
null,
null,
null,
null,
false);
if (ensureResult == null)
{
result.ErrorCode = "WF_GET_DATAAREAS_ENSURE_NTFS_GROUPS_FAILED";
result.ErrorMessage = $"Automatic NTFS permission group ensure failed for '{folderPath}' because the provider returned no result.";
return false;
}
if (ensureResult.resultErrorId != 0)
{
result.ErrorCode = "WF_GET_DATAAREAS_ENSURE_NTFS_GROUPS_FAILED";
result.ErrorMessage = $"Automatic NTFS permission group ensure failed for '{folderPath}': {ensureResult.resultMessage}";
return false;
}
await ntfsArea.ResolvePermissionGroupsAsync(folderPath);
}
return true;
}
private static bool IsAdditionalConfigurationEnabled(cLiamProviderBase provider, string key)
{
if (provider?.AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
return false;
if (!provider.AdditionalConfiguration.TryGetValue(key, out var rawValue) || string.IsNullOrWhiteSpace(rawValue))
return false;
return rawValue.Equals("true", StringComparison.OrdinalIgnoreCase)
|| rawValue.Equals("1", StringComparison.OrdinalIgnoreCase)
|| rawValue.Equals("yes", StringComparison.OrdinalIgnoreCase);
}
private static void SetErrorFromProvider(GetDataAreasOperationResult result, cLiamProviderBase provider, string fallbackCode, string fallbackMessage)
{
var error = ExtractProviderError(provider, fallbackCode, fallbackMessage);
result.ErrorCode = error.Item1;
result.ErrorMessage = error.Item2;
}
private static void SetErrorFromProvider(GetSecurityGroupsOperationResult result, cLiamProviderBase provider, string fallbackCode, string fallbackMessage)
{
var error = ExtractProviderError(provider, fallbackCode, fallbackMessage);
result.ErrorCode = error.Item1;
result.ErrorMessage = error.Item2;
}
private static Tuple<string, string> ExtractProviderError(cLiamProviderBase provider, string fallbackCode, string fallbackMessage)
{
if (provider is cLiamProviderExchange exchangeProvider)
{
var code = exchangeProvider.GetLastErrorCode();
var message = exchangeProvider.GetLastErrorMessage();
if (!string.IsNullOrWhiteSpace(code) || !string.IsNullOrWhiteSpace(message))
{
return Tuple.Create(
string.IsNullOrWhiteSpace(code) ? fallbackCode : code,
string.IsNullOrWhiteSpace(message) ? fallbackMessage : message);
}
}
var providerMessage = provider?.GetLastErrorMessage();
return Tuple.Create(
fallbackCode,
string.IsNullOrWhiteSpace(providerMessage) ? fallbackMessage : providerMessage);
}
private static DataAreaEntry MapDataAreaEntry(cLiamDataAreaBase dataArea, string configurationId)
{
var ntfsPermissionArea = dataArea as cLiamNtfsPermissionDataAreaBase;
var ntfsFolder = dataArea as cLiamNtfsFolder;
var adGroup = dataArea as cLiamAdGroupAsDataArea;
var exchangeMailbox = dataArea as cLiamExchangeSharedMailbox;
var exchangeDistribution = dataArea as cLiamExchangeDistributionGroup;
var owner = exchangeMailbox?.OwnerGroupIdentifier
?? exchangeDistribution?.OwnerGroupIdentifier
?? adGroup?.ManagedBySID
?? ntfsPermissionArea?.OwnerGroupIdentifier
?? string.Empty;
var write = exchangeMailbox != null
? exchangeMailbox.FullAccessGroupSid
: exchangeDistribution != null
? exchangeDistribution.MemberGroupSid
: adGroup?.UID
?? ntfsPermissionArea?.WriteGroupIdentifier
?? string.Empty;
var read = exchangeMailbox != null
? exchangeMailbox.SendAsGroupSid
: ntfsPermissionArea?.ReadGroupIdentifier
?? string.Empty;
var traverse = ntfsPermissionArea?.TraverseGroupIdentifier ?? string.Empty;
var created = ntfsPermissionArea?.CreatedDate ?? DateTime.MinValue.ToString("o");
var description = adGroup?.Description ?? string.Empty;
return new DataAreaEntry
{
DisplayName = dataArea.DisplayName ?? string.Empty,
UID = dataArea.UID ?? string.Empty,
TechnicalName = dataArea.TechnicalName ?? string.Empty,
Description = description,
TargetType = ((int)dataArea.Provider.ProviderType).ToString(),
ParentUID = dataArea.ParentUID ?? string.Empty,
Level = dataArea.Level.ToString(),
Owner = owner,
Write = write,
Read = read,
Traverse = traverse,
CreatedDate = created,
ConfigurationId = configurationId ?? string.Empty,
BaseFolder = ntfsFolder?.Share?.TechnicalName ?? dataArea.Provider?.RootPath ?? string.Empty,
UniqueId = dataArea.UID ?? string.Empty,
DataAreaType = dataArea.DataType.ToString()
};
}
private static SecurityGroupEntry MapSecurityGroupEntry(cLiamDataAreaBase securityGroup)
{
var entry = new SecurityGroupEntry
{
DisplayName = securityGroup.TechnicalName,
TechnicalName = securityGroup.UID,
TargetType = ((int)securityGroup.Provider.ProviderType).ToString()
};
switch (securityGroup)
{
case cLiamAdGroup adGroup:
entry.UID = adGroup.dn;
entry.Scope = adGroup.scope;
break;
case cLiamAdGroup2 adGroup2:
entry.UID = adGroup2.dn;
entry.Scope = adGroup2.scope;
break;
case cLiamExchangeSecurityGroup exchangeGroup:
entry.UID = exchangeGroup.dn;
break;
}
return entry;
}
}
}