1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
using System.Collections.Concurrent;
using ArcaneLibs.Extensions;
using Elastic.Apm;
using Elastic.Apm.Api;
using LibMatrix;
using LibMatrix.Homeservers;
using LibMatrix.RoomTypes;
using LibMatrix.Services;
using MxApiExtensions.Services;
namespace ModAS.Server.Services;
public class UserProviderService(
HomeserverProviderService homeserverProviderService,
IHttpContextAccessor request,
ModASConfiguration config,
AppServiceRegistration asRegistration
) {
public HttpContext? _context = request.HttpContext;
private static ITransaction currentTransaction => Agent.Tracer.CurrentTransaction;
private SemaphoreSlim updateLock = new(1,1);
public ConcurrentDictionary<string, AuthenticatedHomeserverGeneric> KnownUsers { get; set; } = new();
public ConcurrentDictionary<string, DateTime> UserValidationExpiry { get; set; } = new();
public ConcurrentDictionary<string, List<string>> CachedUserRooms { get; set; } = new();
public ConcurrentDictionary<string, DateTime> CachedUserRoomsExpiry { get; set; } = new();
public async Task<AuthenticatedHomeserverGeneric> GetImpersonatedHomeserver(string mxid) {
var span = currentTransaction.StartSpan("GetImpersonatedHomeserver", ApiConstants.TypeApp);
if (!KnownUsers.TryGetValue(mxid, out var homeserver)) {
var getUserSpan = currentTransaction.StartSpan($"GetUser - {mxid}", ApiConstants.TypeApp);
homeserver = await homeserverProviderService.GetAuthenticatedWithToken(config.ServerName, asRegistration.AppServiceToken, config.HomeserverUrl, mxid);
KnownUsers.TryAdd(mxid, homeserver);
getUserSpan.End();
}
await homeserver.SetImpersonate(mxid);
span.End();
return homeserver;
}
public async Task<Dictionary<string, AuthenticatedHomeserverGeneric>> GetValidUsers() {
var span = currentTransaction.StartSpan("GetValidUsers", ApiConstants.TypeApp);
var tasks = KnownUsers.Select(kvp => ValidateUser(kvp.Key, kvp.Value)).ToList();
var results = await Task.WhenAll(tasks);
var validUsers = results.Where(r => r.Value is not null).ToDictionary(r => r.Key, r => r.Value!);
span.End();
return validUsers;
}
public async Task<List<GenericRoom>> GetUserRoomsCached(string mxid) {
var span = currentTransaction.StartSpan($"GetUserRoomsCached - {mxid}", ApiConstants.TypeApp);
var hs = await GetImpersonatedHomeserver(mxid);
if (CachedUserRoomsExpiry.TryGetValue(mxid, out var expiry) && expiry > DateTime.Now) {
if (CachedUserRooms.TryGetValue(mxid, out var rooms)) {
span.End();
return rooms.Select(hs.GetRoom).ToList();
}
}
var userRooms = await hs.GetJoinedRooms();
await updateLock.WaitAsync();
CachedUserRooms[mxid] = userRooms.Select(r => r.RoomId).ToList();
CachedUserRoomsExpiry[mxid] = DateTime.Now + TimeSpan.FromMinutes(5);
updateLock.Release();
span.End();
return userRooms;
}
private async Task<KeyValuePair<string, AuthenticatedHomeserverGeneric?>> ValidateUser(string mxid, AuthenticatedHomeserverGeneric hs) {
if(UserValidationExpiry.TryGetValue(mxid, out var expires))
if(DateTime.Now < expires) return new KeyValuePair<string, AuthenticatedHomeserverGeneric?>(mxid, hs);
var span = currentTransaction.StartSpan($"ValidateUser - {mxid}", ApiConstants.TypeApp);
try {
await hs.GetJoinedRooms();
await updateLock.WaitAsync();
UserValidationExpiry[mxid] = DateTime.Now + TimeSpan.FromMinutes(5);
updateLock.Release();
return new KeyValuePair<string, AuthenticatedHomeserverGeneric?>(mxid, hs);
}
catch (MatrixException e) {
if (e.ErrorCode == "M_FORBIDDEN") {
return new KeyValuePair<string, AuthenticatedHomeserverGeneric?>(mxid, null);
}
throw;
}
finally {
span.End();
}
}
}
|