using System.Collections.Immutable; using Connected; using Connected.Entities; using Connected.Entities.Storage; using Connected.Notifications.Events; using Connected.Security.Cryptography; using Connected.Security.Identity; using Connected.ServiceModel; using Connected.Services; namespace Common.Security.Identity; internal sealed class QueryUsers : ServiceFunction?> { public QueryUsers(IUserCache cache) { Cache = cache; } private IUserCache Cache { get; } protected override async Task?> OnInvoke() { return await (from dc in Cache select dc).AsEntities(); } } internal sealed class SelectUser : ServiceFunction, IUser?> { public SelectUser(IUserCache cache) { Cache = cache; } private IUserCache Cache { get; } protected override async Task OnInvoke() { return await (from dc in Cache where dc.Id == Arguments.Id select dc).AsEntity(); } } /// /// Resolves user by a specified criteria string /// internal sealed class ResolveUser : ServiceFunction { public ResolveUser(IUserCache cache) { Cache = cache; } private IUserCache Cache { get; } protected override async Task OnInvoke() { /* * First, try to resolve by login name */ if (await (from dc in Cache where string.Equals(dc.LoginName, Arguments.Criteria, StringComparison.OrdinalIgnoreCase) select dc).AsEntity() is IUser user) return user; /* * Next, try by authentication token */ if (Guid.TryParse(Arguments.Criteria, out Guid authenticationToken)) { if (await (from dc in Cache where dc.AuthenticationToken == authenticationToken select dc).AsEntity() is IUser authUser) return authUser; } /* * Next, try by email */ if (Arguments.Criteria?.Contains('@') == true) { if (await (from dc in Cache where string.Equals(dc.Email, Arguments.Criteria, StringComparison.OrdinalIgnoreCase) select dc).AsEntity() is IUser emailUser) return emailUser; } /* * Now by id */ if (int.TryParse(Arguments.Criteria, out int id)) { if (await (from dc in Cache where dc.Id == id select dc).AsEntity() is IUser idUser) return idUser; } /* * Doesn't exist. */ return null; } } internal sealed class LookupUsers : ServiceFunction, ImmutableList?> { public LookupUsers(IUserCache cache) { Cache = cache; } private IUserCache Cache { get; } protected override async Task?> OnInvoke() { if (Arguments.IdList is null) return default; return await (from dc in Cache where Arguments.IdList.Contains(dc.Id) select dc).AsEntities(); } } internal sealed class InsertUser : ServiceFunction { public InsertUser(IUserService userService, IUserCache cache, IStorageProvider storage, IEventService events) { UserService = userService; Cache = cache; Storage = storage; Events = events; } private IUserCache Cache { get; } private IStorageProvider Storage { get; } private IEventService Events { get; } private IUserService UserService { get; } protected override async Task OnInvoke() { if (Arguments.AsEntity(State.New) is not User entity) throw EntityExceptions.EntityCastException(Arguments.GetType(), typeof(User)); var result = await Storage.Open().Update(entity); return result.Id; } protected override async Task OnCommitted() { await Cache.Refresh(Result); await Events.Enqueue(this, UserService, ServiceEvents.Inserted, Result); } } internal sealed class DeleteUser : ServiceAction> { public DeleteUser(IUserService userService, IStorageProvider storage, IUserCache cache, IEventService events) { UserService = userService; Storage = storage; Cache = cache; Events = events; } private IUserService UserService { get; } private IStorageProvider Storage { get; } private IUserCache Cache { get; } private IEventService Events { get; } protected override async Task OnInvoke() { await Storage.Open().Update(new User { Id = Arguments.Id, State = State.Deleted }); } protected override async Task OnCommitted() { await Cache.Remove(Arguments.Id); await Events.Enqueue(this, UserService, ServiceEvents.Deleted, Arguments.Id); } } internal sealed class UpdateUser : ServiceAction { public UpdateUser(IUserService userService, IUserCache cache, IStorageProvider storage, IEventService events) { UserService = userService; Cache = cache; Storage = storage; Events = events; } private IUserService UserService { get; } private IUserCache Cache { get; } private IStorageProvider Storage { get; } private IEventService Events { get; } protected override async Task OnInvoke() { await Storage.Open().Update(await Load(), Arguments, async () => { await Cache.Refresh(Arguments.Id); return await Load(); }); } private async Task Load() => await (from dc in Cache where dc.Id == Arguments.Id select dc).AsEntity(); protected override async Task OnCommitted() { await Cache.Refresh(Arguments.Id); await Events.Enqueue(this, UserService, ServiceEvents.Updated, Arguments.Id); } } internal sealed class UserUpdatePassword : ServiceAction { public UserUpdatePassword(IUserService userService, ICryptographyService cryptographyService, IStorageProvider storage, IUserCache cache, IEventService events) { UserService = userService; CryptographyService = cryptographyService; Storage = storage; Cache = cache; Events = events; } private IUserService UserService { get; } private ICryptographyService CryptographyService { get; } private IStorageProvider Storage { get; } public IUserCache Cache { get; } public IEventService Events { get; } protected override async Task OnInvoke() { await Storage.Open().Update(await Load(), Arguments, async () => { await Cache.Refresh(Arguments.Id); return await Load(); }); } private async Task Load() => await (from dc in Cache where dc.Id == Arguments.Id select dc).AsEntity(); protected override async Task OnCommitted() { await Cache.Refresh(Arguments.Id); await Events.Enqueue(this, UserService, ServiceEvents.Updated, Arguments.Id); } }