User synchronization revamped.
This commit is contained in:
10
README.md
10
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.
|
||||
|
||||
|
||||
@ -53,16 +53,22 @@ namespace MessengerApi.Configuration.Model
|
||||
/// </summary>
|
||||
public HousekeepingMessageStates HousekeepingMessageState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// File containing all user credentials.
|
||||
/// </summary>
|
||||
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<int>(config, Env.HOUSEKEEPING_MESSAGE_AGE_IN_MINUTES, x => this.HousekeepingMessageAgeInMinutes = x);
|
||||
Populate<string>(config, Env.HOUSEKEEPING_MESSAGE_STATE, x => this.HousekeepingMessageState = HousekeepingMessageStateParser.Parse(x));
|
||||
Populate<string>(config, Env.LOGGING_VERBOSITY, x => this.Verbosity = LoggingVerbosityParser.Parse(x));
|
||||
Populate<string>(config, Env.USERSCONFIG_FILE_PATH, x => this.UsersConfig = new FileInfo(x));
|
||||
|
||||
void Populate<T>(IEnvironmentConfigurationSource config, string key, Action<T> set)
|
||||
{
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
263
code/MessengerApi/Handlers/UserConfigHandler.cs
Normal file
263
code/MessengerApi/Handlers/UserConfigHandler.cs
Normal file
@ -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<UserIngestionItem[]> 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<Tuple<UserIngestionItem, string>>();
|
||||
|
||||
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<UserIngestionItem, string>(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<UserIngestionOperation[]> 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<UserIngestionOperation>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<UserSetupItem[]> ReadLines(string[] lines)
|
||||
{
|
||||
var items = new List<UserSetupItem>();
|
||||
|
||||
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<UserSetupItem> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MessengerApi.Configuration\MessengerApi.Configuration.csproj" />
|
||||
<ProjectReference Include="..\MessengerApi.Contracts\MessengerApi.Contracts.csproj" />
|
||||
<ProjectReference Include="..\MessengerApi.Db.Npg\MessengerApi.Db.Npg.csproj" />
|
||||
<ProjectReference Include="..\MessengerApi.Db.Sql\MessengerApi.Db.Sql.csproj" />
|
||||
<ProjectReference Include="..\MessengerApi.Db\MessengerApi.Db.csproj" />
|
||||
|
||||
15
code/MessengerApi/Models/UserIngestionItem.cs
Normal file
15
code/MessengerApi/Models/UserIngestionItem.cs
Normal file
@ -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; }
|
||||
}
|
||||
}
|
||||
21
code/MessengerApi/Models/UserIngestionOperation.cs
Normal file
21
code/MessengerApi/Models/UserIngestionOperation.cs
Normal file
@ -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<MessengerDbContext> Operation { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var value = $"Operation: {this.Type}/{this.Subtype} for {this.Username}.";
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
code/MessengerApi/Models/UserIngestionOperationSubtypes.cs
Normal file
14
code/MessengerApi/Models/UserIngestionOperationSubtypes.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace MessengerApi.Models
|
||||
{
|
||||
public enum UserIngestionOperationSubtypes
|
||||
{
|
||||
Unknown,
|
||||
ChangeUserName,
|
||||
ChangeUserApiKey,
|
||||
ChangeUserIsEnabled,
|
||||
AddUser,
|
||||
AddUserRoute,
|
||||
RemoveUser,
|
||||
RemoveUserRoute
|
||||
}
|
||||
}
|
||||
10
code/MessengerApi/Models/UserIngestionOperationTypes.cs
Normal file
10
code/MessengerApi/Models/UserIngestionOperationTypes.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace MessengerApi.Models
|
||||
{
|
||||
public enum UserIngestionOperationTypes
|
||||
{
|
||||
Unknown,
|
||||
Create,
|
||||
Update,
|
||||
Delete
|
||||
}
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -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<ILogger>(new Factories.LoggerFactory(configuration).CreateLogger());
|
||||
builder.Services.AddSingleton<SendEndpointHandler>();
|
||||
builder.Services.AddSingleton<HousekeepingHandler>();
|
||||
builder.Services.AddSingleton<UserSetupHandler>();
|
||||
builder.Services.AddSingleton<UserConfigHandler>();
|
||||
builder.Services.AddSingleton<IDbContextFactory, DbContextFactory>();
|
||||
|
||||
builder.Services.AddScoped<Timing>();
|
||||
@ -142,9 +143,13 @@ namespace MessengerApi.Api
|
||||
}
|
||||
|
||||
// User synchronization
|
||||
var userSetupHandler = app.Services.GetRequiredService<UserSetupHandler>();
|
||||
userSetupHandler.UpdateFromFile(new FileInfo(Constants.USERFILE_FILENAME)).GetAwaiter().GetResult();
|
||||
|
||||
if (configuration.UsersConfig != null)
|
||||
{
|
||||
app.Services.GetRequiredService<ILogger>().Info($"Running userconfig synchronization for {configuration.UsersConfig.FullName}.");
|
||||
var handler = app.Services.GetRequiredService<UserConfigHandler>();
|
||||
handler.UpdateFromFile(configuration.UsersConfig).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
|
||||
@ -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",
|
||||
|
||||
Reference in New Issue
Block a user