diff --git a/README.md b/README.md
index da3f13d..2f84c3f 100644
--- a/README.md
+++ b/README.md
@@ -68,12 +68,12 @@ Additional tunables, with their sustainable default values:
Manage users and credentials in DB manually.
-_Alternatively_, mount a config to the container's root dir with name of `users.conf` with structure that contains `username;id;apikey;comma_separated_allowed_recipients_if_any` per line:
+_Alternatively_, mount a config to the container's root dir with name of `users.conf` with structure that contains `username;is_enabled;id;apikey;comma_separated_allowed_recipients_if_any` per line:
- user1;90ddab90-0b73-4c6c-8dcb-2d8d1ec3c0b8;81ccf737-d424-4f83-929c-92d20491abfa;user2,user3,user4
- user2;8f5971c3-5e19-4b5c-88a7-e0ec4856ce44;f480568f-8884-47e5-a6d7-82480f1ffb3b;user1
- user3;f253a157-f336-4029-b90e-80a9f64b453b;46b882b7-4b96-4fa2-ba1b-4955a9500c36
- user4;5f20ec92-3168-4df5-b20d-5441d08b3f9a;51d11e51-efb2-43e9-beb8-52fb8e879bee;user2
+ user1;true;90ddab90-0b73-4c6c-8dcb-2d8d1ec3c0b8;81ccf737-d424-4f83-929c-92d20491abfa;user2,user3,user4
+ user2;true;8f5971c3-5e19-4b5c-88a7-e0ec4856ce44;f480568f-8884-47e5-a6d7-82480f1ffb3b;user1
+ user3;true;f253a157-f336-4029-b90e-80a9f64b453b;46b882b7-4b96-4fa2-ba1b-4955a9500c36
+ user4;true;5f20ec92-3168-4df5-b20d-5441d08b3f9a;51d11e51-efb2-43e9-beb8-52fb8e879bee;user2
Upon launch, Messenger will synchronize contents of the file with the database. Synchronization uses `id` as primary identifier to make it easy to rotate API keys and change names. Synchronization is done `users.conf => db` and treats the config file as single source of truth, meaning data present in the file but not in db will be added to db, and data not present in file, but present in db will be deleted from db. Editing the file and restarting the service will then update the data accordingly.
diff --git a/code/MessengerApi.Configuration/Model/MessengerConfiguration.cs b/code/MessengerApi.Configuration/Model/MessengerConfiguration.cs
index 2709c3c..df1e7e3 100644
--- a/code/MessengerApi.Configuration/Model/MessengerConfiguration.cs
+++ b/code/MessengerApi.Configuration/Model/MessengerConfiguration.cs
@@ -53,16 +53,22 @@ namespace MessengerApi.Configuration.Model
///
public HousekeepingMessageStates HousekeepingMessageState { get; set; }
+ ///
+ /// File containing all user credentials.
+ ///
+ public FileInfo UsersConfig { get; set; }
+
public MessengerConfiguration() { }
public MessengerConfiguration(string[] origins, PersistenceConfiguration persistenceConfiguration)
{
- if(persistenceConfiguration == null)
+ if (persistenceConfiguration == null)
{
throw new ArgumentNullException(nameof(persistenceConfiguration));
}
this.PersistenceConfiguration = persistenceConfiguration;
+ this.UsersConfig = new FileInfo("./users.conf");
this.Origins = origins ?? [];
this.Proxies = [];
this.RateLimitPerMinute = 120;
@@ -84,6 +90,7 @@ namespace MessengerApi.Configuration.Model
Populate(config, Env.HOUSEKEEPING_MESSAGE_AGE_IN_MINUTES, x => this.HousekeepingMessageAgeInMinutes = x);
Populate(config, Env.HOUSEKEEPING_MESSAGE_STATE, x => this.HousekeepingMessageState = HousekeepingMessageStateParser.Parse(x));
Populate(config, Env.LOGGING_VERBOSITY, x => this.Verbosity = LoggingVerbosityParser.Parse(x));
+ Populate(config, Env.USERSCONFIG_FILE_PATH, x => this.UsersConfig = new FileInfo(x));
void Populate(IEnvironmentConfigurationSource config, string key, Action set)
{
diff --git a/code/MessengerApi.Configuration/Sources/Environment/Constants.EnvironmentVariables.cs b/code/MessengerApi.Configuration/Sources/Environment/Constants.EnvironmentVariables.cs
index 438dfc7..db1fc53 100644
--- a/code/MessengerApi.Configuration/Sources/Environment/Constants.EnvironmentVariables.cs
+++ b/code/MessengerApi.Configuration/Sources/Environment/Constants.EnvironmentVariables.cs
@@ -15,6 +15,7 @@
public const string HOUSEKEEPING_MESSAGE_AGE_IN_MINUTES = nameof(HOUSEKEEPING_MESSAGE_AGE_IN_MINUTES);
public const string HOUSEKEEPING_MESSAGE_STATE = nameof(HOUSEKEEPING_MESSAGE_STATE);
public const string LOGGING_VERBOSITY = nameof(LOGGING_VERBOSITY);
+ public const string USERSCONFIG_FILE_PATH = nameof(USERSCONFIG_FILE_PATH);
}
}
}
\ No newline at end of file
diff --git a/code/MessengerApi.sln b/code/MessengerApi.sln
index 85a6f9a..0d36e68 100644
--- a/code/MessengerApi.sln
+++ b/code/MessengerApi.sln
@@ -3,12 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35312.102
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessengerApi.Contracts", "MessengerApi.Contracts\MessengerApi.Contracts.csproj", "{833ED77F-A4E9-4FB3-BB84-4E55898B726A}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessengerApi.SubscriptionClient", "MessengerApi.SubscriptionClient\MessengerApi.SubscriptionClient.csproj", "{127D24B0-47F3-40E9-9136-899AFF206F19}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessengerApi.QueryClient", "MessengerApi.QueryClient\MessengerApi.QueryClient.csproj", "{6441673B-2621-4E2C-A9A0-971E83C3F80A}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{60B75400-A315-4B57-AFCF-5B4094785A62}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessengerApi.Examples.PhonebookClient", "MessengerApi.Examples.PhonebookClient\MessengerApi.Examples.PhonebookClient.csproj", "{D2C7396F-B0CA-4BA5-A07A-C70DB283B3CF}"
@@ -36,6 +30,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\Directory.Packages.props = ..\Directory.Packages.props
..\docker-compose.yml = ..\docker-compose.yml
..\Dockerfile = ..\Dockerfile
+ ..\assets\example-users.config = ..\assets\example-users.config
..\NuGet.config = ..\NuGet.config
EndProjectSection
EndProject
@@ -63,18 +58,6 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {833ED77F-A4E9-4FB3-BB84-4E55898B726A}.Release|Any CPU.Build.0 = Release|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {127D24B0-47F3-40E9-9136-899AFF206F19}.Release|Any CPU.Build.0 = Release|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6441673B-2621-4E2C-A9A0-971E83C3F80A}.Release|Any CPU.Build.0 = Release|Any CPU
{D2C7396F-B0CA-4BA5-A07A-C70DB283B3CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2C7396F-B0CA-4BA5-A07A-C70DB283B3CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2C7396F-B0CA-4BA5-A07A-C70DB283B3CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/code/MessengerApi/Handlers/UserConfigHandler.cs b/code/MessengerApi/Handlers/UserConfigHandler.cs
new file mode 100644
index 0000000..17ffaa8
--- /dev/null
+++ b/code/MessengerApi/Handlers/UserConfigHandler.cs
@@ -0,0 +1,263 @@
+using MessengerApi.Configuration.Model;
+using MessengerApi.Contracts.Factories;
+using MessengerApi.Db.Entities;
+using MessengerApi.Models;
+using Microsoft.EntityFrameworkCore;
+using System.Text;
+
+namespace MessengerApi.Handlers
+{
+ public class UserConfigHandler
+ {
+ private readonly MessengerConfiguration configuration;
+ private readonly ILogger logger;
+ private readonly IDbContextFactory dbContextFactory;
+
+ public UserConfigHandler(
+ MessengerConfiguration configuration,
+ ILogger logger,
+ IDbContextFactory dbContextFactory)
+ {
+ this.configuration = configuration;
+ this.logger = logger;
+ this.dbContextFactory = dbContextFactory;
+ }
+
+ public async Task GetItemsFromFile(FileInfo file)
+ {
+ if (file == null)
+ {
+ throw new InvalidOperationException("No file provided.");
+ }
+ else if (File.Exists(file.FullName) == false)
+ {
+ throw new FileNotFoundException($"Userfile doesn't exist: {this.configuration.UsersConfig.FullName}.");
+ }
+
+ var lines = await File.ReadAllLinesAsync(file.FullName, Encoding.UTF8);
+ var collection = new List>();
+
+ foreach (var line in lines)
+ {
+ var columns = line.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+ var item = new UserIngestionItem();
+ item.UserName = columns[0];
+ item.IsEnabled = bool.Parse(columns[1]);
+ item.Id = Guid.Parse(columns[2]);
+ item.ApiKey = Guid.Parse(columns[3]);
+
+ var recipients = columns.Length == 5
+ ? columns[4]
+ : null;
+
+ collection.Add(new Tuple(item, recipients));
+
+ this.logger.Trace($"Reading user {item.UserName} with ID {item.Id}");
+ }
+
+ foreach (var item in collection)
+ {
+ if(item.Item2 == null)
+ {
+ item.Item1.CanSendTo = [];
+ continue;
+ }
+
+ var names = item.Item2.Split(
+ ',',
+ StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+
+ item.Item1.CanSendTo = names
+ .Select(x => collection.Single(c => c.Item1.UserName == x).Item1)
+ .ToArray();
+
+ this.logger.Trace($"User {item.Item1.UserName} has buddies {item.Item2}.");
+ }
+
+ return collection.Select(x => x.Item1).ToArray();
+ }
+
+ public Task GetOperationsFromItems(UserIngestionItem[] fileItems)
+ {
+ if(fileItems == null)
+ {
+ throw new ArgumentNullException(nameof(fileItems));
+ }
+
+ using var context = this.dbContextFactory.CreateDbContext();
+
+ var existingUsers = context.Users.ToList();
+ var existingRoutes = context.UserRoutes.Include(x => x.From).Include(x => x.To).ToList();
+
+ var collection = new List();
+
+ foreach (var item in fileItems)
+ {
+ var dbItem = context.Users.SingleOrDefault(x => x.Id == item.Id);
+
+ if (dbItem == null)
+ {
+ collection.Add(new UserIngestionOperation
+ {
+ Type = UserIngestionOperationTypes.Create,
+ Subtype = UserIngestionOperationSubtypes.AddUser,
+ Username = item.UserName,
+ Operation = (c) =>
+ {
+ c.Users.Local.Add(new User
+ {
+ Id = item.Id,
+ ApiKey = item.ApiKey,
+ IsEnabled = item.IsEnabled,
+ Name = item.UserName,
+ });
+ }
+ });
+ }
+ else
+ {
+ if(dbItem.ApiKey != item.ApiKey)
+ {
+ collection.Add(new UserIngestionOperation
+ {
+ Type = UserIngestionOperationTypes.Update,
+ Subtype = UserIngestionOperationSubtypes.ChangeUserApiKey,
+ Username = item.UserName,
+ Operation = (c) =>
+ {
+ c.Users.Single(x => x.Id == item.ApiKey).ApiKey = item.ApiKey;
+ }
+ });
+ }
+
+ if(dbItem.IsEnabled != item.IsEnabled)
+ {
+ collection.Add(new UserIngestionOperation
+ {
+ Type = UserIngestionOperationTypes.Update,
+ Subtype = UserIngestionOperationSubtypes.ChangeUserIsEnabled,
+ Username = item.UserName,
+ Operation = (c) =>
+ {
+ c.Users.Single(x => x.Id == item.ApiKey).IsEnabled = item.IsEnabled;
+ }
+ });
+ }
+
+ if (dbItem.Name != item.UserName)
+ {
+ collection.Add(new UserIngestionOperation
+ {
+ Type = UserIngestionOperationTypes.Update,
+ Subtype = UserIngestionOperationSubtypes.ChangeUserName,
+ Username = item.UserName,
+ Operation = (c) =>
+ {
+ c.Users.Single(x => x.Id == item.ApiKey).Name = item.UserName;
+ }
+ });
+ }
+ }
+
+ this.logger.Trace($"Processed line with {item.UserName}.");
+ }
+
+ this.logger.Debug("Finished evaluating add/update users, doing routes.");
+
+ foreach(var item in fileItems)
+ {
+ foreach(var com in item.CanSendTo)
+ {
+ this.logger.Trace($"Validating if {item.UserName} can send to {com.UserName}.");
+
+ var dbRoute = context.UserRoutes
+ .Include(x => x.From)
+ .Include(x => x.To)
+ .SingleOrDefault(x => x.From.Id == item.Id && x.To.Id == com.Id);
+
+ if(dbRoute == null)
+ {
+ collection.Add(new UserIngestionOperation
+ {
+ Type = UserIngestionOperationTypes.Create,
+ Subtype = UserIngestionOperationSubtypes.AddUserRoute,
+ Username = item.UserName,
+ Operation = (c) =>
+ {
+ var from = c.Users
+ .SingleOrDefault(x => x.Id == item.Id) ??
+ c.Users.Local.Single(x => x.Id == item.Id);
+
+ var to = c.Users
+ .SingleOrDefault(x => x.Id == com.Id) ??
+ c.Users.Local.Single(x => x.Id == com.Id);
+
+ c.Add(new UserRoute
+ {
+ From = from,
+ To = to
+ });
+ }
+ });
+ }
+ }
+ }
+
+ this.logger.Debug("Finished evaluating add userroutes, doing user deletes.");
+
+ foreach(var existingDbUser in existingUsers)
+ {
+ if(fileItems.Any(x=>x.Id == existingDbUser.Id) == false)
+ {
+ collection.Add(new UserIngestionOperation
+ {
+ Username = existingDbUser.Name,
+ Type = UserIngestionOperationTypes.Delete,
+ Subtype = UserIngestionOperationSubtypes.RemoveUser,
+ Operation = (c) =>
+ {
+ c.Remove(c.Users.Single(x => x.Id == existingDbUser.Id));
+ }
+ });
+ }
+ }
+
+ foreach (var existingRoute in existingRoutes)
+ {
+ if (fileItems.Any(fi => fi.CanSendTo.Any(fic => fi.Id == existingRoute.From.Id && fic.Id == existingRoute.To.Id)) == false)
+ {
+ collection.Add(new UserIngestionOperation
+ {
+ Username = existingRoute.From.Name,
+ Type = UserIngestionOperationTypes.Delete,
+ Subtype = UserIngestionOperationSubtypes.RemoveUserRoute,
+ Operation = (c) =>
+ {
+ c.Remove(c.UserRoutes.Single(x => x.Id == existingRoute.Id));
+ }
+ });
+ }
+ }
+
+ return Task.FromResult(collection.ToArray());
+ }
+
+ public async Task UpdateFromFile(FileInfo file)
+ {
+ var fileItems = await this.GetItemsFromFile(file);
+ var operations = await this.GetOperationsFromItems(fileItems);
+ using var context = this.dbContextFactory.CreateDbContext();
+
+ foreach(var operation in operations)
+ {
+ this.logger.Info(operation.ToString());
+ operation.Operation(context);
+ }
+
+ if(operations.Any())
+ {
+ await context.SaveChangesAsync();
+ }
+ }
+ }
+}
diff --git a/code/MessengerApi/Handlers/UserSetupHandler.cs b/code/MessengerApi/Handlers/UserSetupHandler.cs
deleted file mode 100644
index 4148995..0000000
--- a/code/MessengerApi/Handlers/UserSetupHandler.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using MessengerApi.Configuration.Model;
-using MessengerApi.Contracts.Factories;
-using MessengerApi.Db;
-using MessengerApi.Models;
-using System.Text;
-
-namespace MessengerApi.Handlers
-{
- // TODO: This needs to be redone, because at every run, it wipes users and creates new ones. This makes
- // all existing DB messages unassignable.
- public class UserSetupHandler
- {
- private readonly MessengerConfiguration configuration;
- private readonly ILogger logger;
- private readonly IDbContextFactory dbContextFactory;
-
- public UserSetupHandler(
- MessengerConfiguration configuration,
- ILogger logger,
- IDbContextFactory dbContextFactory)
- {
- this.configuration = configuration;
- this.logger = logger;
- this.dbContextFactory = dbContextFactory;
- }
-
- public async Task UpdateFromFile(FileInfo file)
- {
- if(file.Exists)
- {
- var lines = await File.ReadAllLinesAsync(file.FullName, Encoding.UTF8);
- var items = await this.ReadLines(lines);
-
- await this.SynchronizeUsers(items);
- }
- }
-
- private async Task ReadLines(string[] lines)
- {
- var items = new List();
-
- foreach (var line in lines)
- {
- var values = line.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- var item = new UserSetupItem
- {
- UserName = values[0],
- ApiKey = values[1],
- };
-
- if(values.Length > 2)
- {
- item.CanSendToUserNames = values[2].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- }
-
- items.Add(item);
- }
-
- if (items.GroupBy(x => x.UserName).Any(x => x.Count() > 1))
- {
- throw new InvalidOperationException("Usernames are not unique. One username per line.");
- }
- else if(items.GroupBy(x=>x.ApiKey).Any(x=>x.Count() > 1))
- {
- throw new InvalidOperationException("API keys are not unique. One API key per line.");
- }
-
- return items.ToArray();
- }
-
- private Task SynchronizeUsers(IEnumerable users)
- {
- using var db = this.dbContextFactory.CreateDbContext();
- db.RemoveRange(db.Users);
- db.RemoveRange(db.UserRoutes);
-
- var dbUsers = users.Select(x => new Db.Entities.User
- {
- Id = new Guid(),
- Name = x.UserName,
- ApiKey = Guid.Parse(x.ApiKey),
- IsEnabled = true
- });
-
- var dbRoutes = users.SelectMany(x => x.CanSendToUserNames.Select(cs => new Db.Entities.UserRoute
- {
- Id = new Guid(),
- From = dbUsers.Single(dbu => dbu.Name == x.UserName),
- To = dbUsers.Single(dbu => dbu.Name == x.UserName)
- }));
-
- db.AddRange(dbUsers);
- db.AddRange(dbRoutes);
-
- db.SaveChanges();
- return Task.CompletedTask;
- }
- }
-}
diff --git a/code/MessengerApi/MessengerApi.csproj b/code/MessengerApi/MessengerApi.csproj
index 16f652b..2e8b271 100644
--- a/code/MessengerApi/MessengerApi.csproj
+++ b/code/MessengerApi/MessengerApi.csproj
@@ -21,7 +21,6 @@
-
diff --git a/code/MessengerApi/Models/UserIngestionItem.cs b/code/MessengerApi/Models/UserIngestionItem.cs
new file mode 100644
index 0000000..ce29543
--- /dev/null
+++ b/code/MessengerApi/Models/UserIngestionItem.cs
@@ -0,0 +1,15 @@
+namespace MessengerApi.Models
+{
+ public class UserIngestionItem
+ {
+ public string UserName { get; set; }
+
+ public Guid Id { get; set; }
+
+ public Guid ApiKey { get; set; }
+
+ public bool IsEnabled { get; set; }
+
+ public UserIngestionItem[] CanSendTo { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/code/MessengerApi/Models/UserIngestionOperation.cs b/code/MessengerApi/Models/UserIngestionOperation.cs
new file mode 100644
index 0000000..268dfc8
--- /dev/null
+++ b/code/MessengerApi/Models/UserIngestionOperation.cs
@@ -0,0 +1,21 @@
+using MessengerApi.Db;
+
+namespace MessengerApi.Models
+{
+ public class UserIngestionOperation
+ {
+ public UserIngestionOperationTypes Type { get; set; }
+
+ public UserIngestionOperationSubtypes Subtype { get; set; }
+
+ public string Username { get; set; }
+
+ public Action Operation { get; set; }
+
+ public override string ToString()
+ {
+ var value = $"Operation: {this.Type}/{this.Subtype} for {this.Username}.";
+ return value;
+ }
+ }
+}
diff --git a/code/MessengerApi/Models/UserIngestionOperationSubtypes.cs b/code/MessengerApi/Models/UserIngestionOperationSubtypes.cs
new file mode 100644
index 0000000..a00ffcc
--- /dev/null
+++ b/code/MessengerApi/Models/UserIngestionOperationSubtypes.cs
@@ -0,0 +1,14 @@
+namespace MessengerApi.Models
+{
+ public enum UserIngestionOperationSubtypes
+ {
+ Unknown,
+ ChangeUserName,
+ ChangeUserApiKey,
+ ChangeUserIsEnabled,
+ AddUser,
+ AddUserRoute,
+ RemoveUser,
+ RemoveUserRoute
+ }
+}
diff --git a/code/MessengerApi/Models/UserIngestionOperationTypes.cs b/code/MessengerApi/Models/UserIngestionOperationTypes.cs
new file mode 100644
index 0000000..8142fe8
--- /dev/null
+++ b/code/MessengerApi/Models/UserIngestionOperationTypes.cs
@@ -0,0 +1,10 @@
+namespace MessengerApi.Models
+{
+ public enum UserIngestionOperationTypes
+ {
+ Unknown,
+ Create,
+ Update,
+ Delete
+ }
+}
diff --git a/code/MessengerApi/Models/UserSetupItem.cs b/code/MessengerApi/Models/UserSetupItem.cs
deleted file mode 100644
index 2aad706..0000000
--- a/code/MessengerApi/Models/UserSetupItem.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace MessengerApi.Models
-{
- public class UserSetupItem
- {
- public string UserName { get; set; }
-
- public string ApiKey { get; set; }
-
- public string[] CanSendToUserNames { get; set; }
- }
-}
\ No newline at end of file
diff --git a/code/MessengerApi/Program.cs b/code/MessengerApi/Program.cs
index f76f268..dd0be5d 100644
--- a/code/MessengerApi/Program.cs
+++ b/code/MessengerApi/Program.cs
@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Net;
+using System.Runtime.CompilerServices;
using System.Threading.RateLimiting;
namespace MessengerApi.Api
@@ -43,7 +44,7 @@ namespace MessengerApi.Api
builder.Services.AddSingleton(new Factories.LoggerFactory(configuration).CreateLogger());
builder.Services.AddSingleton();
builder.Services.AddSingleton();
- builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddScoped();
@@ -142,9 +143,13 @@ namespace MessengerApi.Api
}
// User synchronization
- var userSetupHandler = app.Services.GetRequiredService();
- userSetupHandler.UpdateFromFile(new FileInfo(Constants.USERFILE_FILENAME)).GetAwaiter().GetResult();
-
+ if (configuration.UsersConfig != null)
+ {
+ app.Services.GetRequiredService().Info($"Running userconfig synchronization for {configuration.UsersConfig.FullName}.");
+ var handler = app.Services.GetRequiredService();
+ handler.UpdateFromFile(configuration.UsersConfig).GetAwaiter().GetResult();
+ }
+
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
diff --git a/code/MessengerApi/Properties/launchSettings.json b/code/MessengerApi/Properties/launchSettings.json
index c29747b..e290344 100644
--- a/code/MessengerApi/Properties/launchSettings.json
+++ b/code/MessengerApi/Properties/launchSettings.json
@@ -5,6 +5,7 @@
"launchBrowser": true,
"environmentVariables": {
"PERSISTENCE_TYPE": "Sql",
+ "USERSCONFIG_FILE_PATH": "./../../assets/example-users.config",
"CORS_ORIGINS": "",
"PROXIES": "",
"QUERY_RATE_PER_MINUTE": "100",