Skip to content

Commit

Permalink
user sync
Browse files Browse the repository at this point in the history
  • Loading branch information
cammj committed May 9, 2024
1 parent b28b45a commit 66aee8a
Showing 1 changed file with 108 additions and 4 deletions.
112 changes: 108 additions & 4 deletions Sync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ public static class Sync

private static string _appClientId = Environment.GetEnvironmentVariable("AppClientId", EnvironmentVariableTarget.Process);
private static string _appTenantId = Environment.GetEnvironmentVariable("AppTenantId", EnvironmentVariableTarget.Process);
private static string _appSecret =Environment.GetEnvironmentVariable("AppSecret", EnvironmentVariableTarget.Process);
private static string _appSecret = Environment.GetEnvironmentVariable("AppSecret", EnvironmentVariableTarget.Process);

// If to pull entra users
private static bool _pullEntraUsers =
bool.Parse(Environment.GetEnvironmentVariable("SyncEntra", EnvironmentVariableTarget.Process) ?? "false");

// If to pull entra users
private static string _pullEntraProperties = (Environment.GetEnvironmentVariable("SyncEntraProperties", EnvironmentVariableTarget.Process) ?? "id,mail,displayName,givenName,surname,department,companyName,city,country,JobTitle");

/// <summary>
/// How many table rows to send up in a batch
Expand All @@ -40,6 +47,16 @@ public static class Sync
/// </summary>
private static ConcurrentQueue<TableTransactionAction> tableActionQueue_SimulationUserEvents = new();

/// <summary>
/// Queue for User entries, async batch/bulk processed
/// </summary>
private static ConcurrentQueue<TableTransactionAction> tableActionQueue_Users = new();

/// <summary>
/// Maintains a list of users we have already synced
/// </summary>
private static ConcurrentDictionary<string, bool> userListSynced = new();

/// <summary>
/// Logger
/// </summary>
Expand Down Expand Up @@ -106,12 +123,16 @@ private static async Task BatchQueueProcessor(CancellationToken cancellationToke
if (tableActionQueue_SimulationUsers.Count > _maxTableBatchSize)
SubmissionTasks.Add(Task.Run(() => BatchQueueProcess(tableActionQueue_SimulationUsers,
new TableClient(GetStorageConnection(), "SimulationUsers"), cancellationToken)));



// Process SimulationUserEvents Queue
if (tableActionQueue_SimulationUserEvents.Count > _maxTableBatchSize)
SubmissionTasks.Add(Task.Run(() => BatchQueueProcess(tableActionQueue_SimulationUserEvents,
new TableClient(GetStorageConnection(), "SimulationUserEvents"), cancellationToken)));

// Process Users Queue
if (tableActionQueue_Users.Count > _maxTableBatchSize)
SubmissionTasks.Add(Task.Run(() => BatchQueueProcess(tableActionQueue_Users,
new TableClient(GetStorageConnection(), "Users"), cancellationToken)));

// Wait for all submission tasks to complete
await Task.WhenAll(SubmissionTasks);
Expand All @@ -122,14 +143,16 @@ private static async Task BatchQueueProcessor(CancellationToken cancellationToke

// Flush all queues if not empty
while (!tableActionQueue_SimulationUsers.IsEmpty && !tableActionQueue_SimulationUserEvents.IsEmpty &&
!tableActionQueue_SimulationUserEvents.IsEmpty)
!tableActionQueue_SimulationUserEvents.IsEmpty && !tableActionQueue_Users.IsEmpty)
{
await BatchQueueProcess(tableActionQueue_Simulations,
new TableClient(GetStorageConnection(), "Simulations"), cancellationToken);
await BatchQueueProcess(tableActionQueue_SimulationUsers,
new TableClient(GetStorageConnection(), "SimulationUsers"), cancellationToken);
await BatchQueueProcess(tableActionQueue_SimulationUserEvents,
new TableClient(GetStorageConnection(), "SimulationUserEvents"), cancellationToken);
await BatchQueueProcess(tableActionQueue_Users,
new TableClient(GetStorageConnection(), "Users"), cancellationToken);
}

}
Expand Down Expand Up @@ -179,6 +202,7 @@ private static async Task CreateRequiredTables()
await serviceClient.CreateTableIfNotExistsAsync("Simulations");
await serviceClient.CreateTableIfNotExistsAsync("SimulationUsers");
await serviceClient.CreateTableIfNotExistsAsync("SimulationUserEvents");
await serviceClient.CreateTableIfNotExistsAsync("Users");
}

/// <summary>
Expand Down Expand Up @@ -288,6 +312,12 @@ private static async Task GetTenantSimulationUsers(GraphServiceClient GraphClien
{"ReportedPhishDateTime", userSimDetail.ReportedPhishDateTime},
}));

// Determine if should sync user
if (await ShouldSyncUser(userSimDetail.SimulationUser?.UserId))
{
await SyncUser(GraphClient, userSimDetail.SimulationUser?.UserId);
}

// Add simulation user events in to table
if (userSimDetail.SimulationEvents is not null)
{
Expand Down Expand Up @@ -346,4 +376,78 @@ private static GraphServiceClient GetGraphServicesClient()
/// </summary>
/// <returns></returns>
public static string GetStorageConnection() => Environment.GetEnvironmentVariable("AzureWebJobsStorage", EnvironmentVariableTarget.Process);

/// <summary>
/// Synchronise user
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private static async Task SyncUser(GraphServiceClient GraphClient, string id)
{
// Set in dictionary
userListSynced[id] = true;

string[] syncProperties = _pullEntraProperties.Split(",");

try
{
var User = await GraphClient.Users[id].GetAsync(requestConfiguration =>
requestConfiguration.QueryParameters.Select = syncProperties);

if (User is not null)
{
tableActionQueue_Users.Enqueue(new TableTransactionAction(TableTransactionActionType.UpdateReplace, new TableEntity("Users", id)
{
{"DisplayName", User.DisplayName},
{"GivenName", User.GivenName},
{"Surname", User.Surname},
{"Country", User.Country},
{"Mail", User.Mail},
{"Department", User.Department},
{"CompanyName", User.CompanyName},
{"City", User.City},
{"Country", User.Country},
{"JobTitle", User.JobTitle},
{"LastUserSync", DateTime.UtcNow}
}));
}

}
catch (Exception e)
{
_log.LogError($"Failed to sync user {id}: {e}");
}
}

/// <summary>
/// Determine if should sync user
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private static async Task<bool> ShouldSyncUser(string id)
{
// Return false if set not to sync users
if (!_pullEntraUsers)
return false;

// Return false if already synchronised
if (userListSynced.ContainsKey(id))
return false;

// Get the table entity to determine how long ago the user has been pulled
TableClient tableClient = new TableClient(GetStorageConnection(), "Users");
var UserTableItem = await tableClient.GetEntityIfExistsAsync<TableEntity>("Users", id);

// Get last sync time
DateTime? LastUserSync = null;
if (UserTableItem.HasValue && UserTableItem.Value.ContainsKey("LastUserSync"))
LastUserSync = DateTime.Parse(UserTableItem.Value["LastUserSync"].ToString());

// If no sync or days is older than a year
if (LastUserSync is null || LastUserSync.Value < DateTime.UtcNow.AddDays(-1))
return true;

return false;

}
}

0 comments on commit 66aee8a

Please sign in to comment.