diff --git a/Crypter.API/Controllers/ConsentController.cs b/Crypter.API/Controllers/ConsentController.cs index dbe805f18..b06a71262 100644 --- a/Crypter.API/Controllers/ConsentController.cs +++ b/Crypter.API/Controllers/ConsentController.cs @@ -24,9 +24,14 @@ * Contact the current copyright holder to discuss commercial license options. */ +using System; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Crypter.API.Controllers.Base; +using Crypter.Common.Contracts.Features.UserConsents; using Crypter.Core.Features.UserConsent.Commands; +using Crypter.Core.Features.UserConsent.Queries; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -45,14 +50,25 @@ public ConsentController(ISender sender) _sender = sender; } - [HttpPost("recovery-key-risk")] + [HttpGet] + [Authorize] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Dictionary))] + [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(void))] + public async Task GetUserConsentsAsync(CancellationToken cancellationToken) + { + GetUserConsentsQuery query = new GetUserConsentsQuery(UserId); + Dictionary result = await _sender.Send(query, cancellationToken); + return Ok(result); + } + + [HttpPost] [Authorize] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(void))] [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(void))] - public async Task ConsentToRecoveryKeyRisksAsync() + public async Task ConsentAsync(UserConsentRequest request) { - SaveAcknowledgementOfRecoveryKeyRisksCommand request = new SaveAcknowledgementOfRecoveryKeyRisksCommand(UserId); - await _sender.Send(request); + SaveUserConsentCommand command = new SaveUserConsentCommand(UserId, request.ConsentType); + await _sender.Send(command, CancellationToken.None); return Ok(); } } diff --git a/Crypter.Common.Client/HttpClients/CrypterAuthenticatedHttpClient.cs b/Crypter.Common.Client/HttpClients/CrypterAuthenticatedHttpClient.cs index 5e527ce44..630ca893a 100644 --- a/Crypter.Common.Client/HttpClients/CrypterAuthenticatedHttpClient.cs +++ b/Crypter.Common.Client/HttpClients/CrypterAuthenticatedHttpClient.cs @@ -154,6 +154,16 @@ public async Task> PostEitherAsync(response); } + public async Task> PostMaybeUnitResponseAsync(string uri, TRequest body) + where TRequest : class + { + Func requestFactory = MakeRequestMessageFactory(HttpMethod.Post, uri, body); + using HttpResponseMessage response = await SendWithAuthenticationAsync(requestFactory, false); + return response.IsSuccessStatusCode + ? Unit.Default + : Maybe.None; + } + public async Task> PostMaybeUnitResponseAsync(string uri) { Func requestFactory = MakeRequestMessageFactory(HttpMethod.Post, uri); diff --git a/Crypter.Common.Client/HttpClients/CrypterHttpClient.cs b/Crypter.Common.Client/HttpClients/CrypterHttpClient.cs index 3fefe97f1..0882d3f41 100644 --- a/Crypter.Common.Client/HttpClients/CrypterHttpClient.cs +++ b/Crypter.Common.Client/HttpClients/CrypterHttpClient.cs @@ -84,6 +84,16 @@ public async Task> PostEitherAsync(request); } + public async Task> PostMaybeUnitResponseAsync(string uri, TRequest body) + where TRequest : class + { + using HttpRequestMessage request = MakeRequestMessage(HttpMethod.Post, uri, body); + using HttpResponseMessage response = await _httpClient.SendAsync(request); + return response.IsSuccessStatusCode + ? Unit.Default + : Maybe.None; + } + public async Task> PostMaybeUnitResponseAsync(string uri) { using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri); diff --git a/Crypter.Common.Client/HttpClients/Requests/UserConsentRequests.cs b/Crypter.Common.Client/HttpClients/Requests/UserConsentRequests.cs index 58cc9fc28..dde08a2c6 100644 --- a/Crypter.Common.Client/HttpClients/Requests/UserConsentRequests.cs +++ b/Crypter.Common.Client/HttpClients/Requests/UserConsentRequests.cs @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Crypter File Transfer + * Copyright (C) 2025 Crypter File Transfer * * This file is part of the Crypter file transfer project. * @@ -24,9 +24,12 @@ * Contact the current copyright holder to discuss commercial license options. */ +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Crypter.Common.Client.Interfaces.HttpClients; using Crypter.Common.Client.Interfaces.Requests; +using Crypter.Common.Contracts.Features.UserConsents; using EasyMonads; namespace Crypter.Common.Client.HttpClients.Requests; @@ -40,9 +43,16 @@ public UserConsentRequests(ICrypterAuthenticatedHttpClient crypterAuthenticatedH _crypterAuthenticatedHttpClient = crypterAuthenticatedHttpClient; } - public Task> ConsentToRecoveryKeyRisksAsync() + public Task>> GetUserConsentsAsync() { - const string url = "api/user/consent/recovery-key-risk"; - return _crypterAuthenticatedHttpClient.PostMaybeUnitResponseAsync(url); + const string url = "api/user/consent"; + return _crypterAuthenticatedHttpClient.GetMaybeAsync>(url); + } + + public Task> ConsentAsync(UserConsentType consentType) + { + const string url = "api/user/consent"; + UserConsentRequest request = new UserConsentRequest(consentType); + return _crypterAuthenticatedHttpClient.PostMaybeUnitResponseAsync(url, request); } } diff --git a/Crypter.Common.Client/HttpClients/Requests/UserSettingRequests.cs b/Crypter.Common.Client/HttpClients/Requests/UserSettingRequests.cs index c21753ee9..f129c8dc2 100644 --- a/Crypter.Common.Client/HttpClients/Requests/UserSettingRequests.cs +++ b/Crypter.Common.Client/HttpClients/Requests/UserSettingRequests.cs @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Crypter File Transfer + * Copyright (C) 2025 Crypter File Transfer * * This file is part of the Crypter file transfer project. * @@ -41,8 +41,7 @@ public class UserSettingRequests : IUserSettingRequests private readonly ICrypterHttpClient _crypterHttpClient; private readonly ICrypterAuthenticatedHttpClient _crypterAuthenticatedHttpClient; - public UserSettingRequests(ICrypterHttpClient crypterHttpClient, - ICrypterAuthenticatedHttpClient crypterAuthenticatedHttpClient) + public UserSettingRequests(ICrypterHttpClient crypterHttpClient, ICrypterAuthenticatedHttpClient crypterAuthenticatedHttpClient) { _crypterHttpClient = crypterHttpClient; _crypterAuthenticatedHttpClient = crypterAuthenticatedHttpClient; @@ -54,8 +53,7 @@ public Task> GetProfileSettingsAsync() return _crypterAuthenticatedHttpClient.GetMaybeAsync(url); } - public Task> SetProfileSettingsAsync( - ProfileSettings newProfileSettings) + public Task> SetProfileSettingsAsync(ProfileSettings newProfileSettings) { const string url = "api/user/setting/profile"; return _crypterAuthenticatedHttpClient.PutEitherAsync(url, newProfileSettings) @@ -68,8 +66,7 @@ public Task> GetContactInfoSettingsAsync() return _crypterAuthenticatedHttpClient.GetMaybeAsync(url); } - public Task> UpdateContactInfoSettingsAsync( - UpdateContactInfoSettingsRequest newContactInfoSettings) + public Task> UpdateContactInfoSettingsAsync(UpdateContactInfoSettingsRequest newContactInfoSettings) { const string url = "api/user/setting/contact"; return _crypterAuthenticatedHttpClient @@ -83,8 +80,7 @@ public Task> GetNotificationSettingsAsync() return _crypterAuthenticatedHttpClient.GetMaybeAsync(url); } - public Task> UpdateNotificationSettingsAsync( - NotificationSettings newNotificationSettings) + public Task> UpdateNotificationSettingsAsync(NotificationSettings newNotificationSettings) { const string url = "api/user/setting/notification"; return _crypterAuthenticatedHttpClient @@ -98,16 +94,14 @@ public Task> GetPrivacySettingsAsync() return _crypterAuthenticatedHttpClient.GetMaybeAsync(url); } - public Task> SetPrivacySettingsAsync( - PrivacySettings newPrivacySettings) + public Task> SetPrivacySettingsAsync(PrivacySettings newPrivacySettings) { const string url = "api/user/setting/privacy"; return _crypterAuthenticatedHttpClient.PutEitherAsync(url, newPrivacySettings) .ExtractErrorCode(); } - public Task> VerifyUserEmailAddressAsync( - VerifyEmailAddressRequest verificationInfo) + public Task> VerifyUserEmailAddressAsync(VerifyEmailAddressRequest verificationInfo) { const string url = "api/user/setting/contact/verify"; return _crypterHttpClient.PostEitherUnitResponseAsync(url, verificationInfo) diff --git a/Crypter.Common.Client/Interfaces/HttpClients/ICrypterHttpClient.cs b/Crypter.Common.Client/Interfaces/HttpClients/ICrypterHttpClient.cs index 50ec57ae7..83958076d 100644 --- a/Crypter.Common.Client/Interfaces/HttpClients/ICrypterHttpClient.cs +++ b/Crypter.Common.Client/Interfaces/HttpClients/ICrypterHttpClient.cs @@ -47,9 +47,12 @@ Task> GetEitherAsync(string uri) Task> PostEitherAsync(string uri, TRequest body) where TRequest : class where TResponse : class; - + Task> PostMaybeUnitResponseAsync(string uri); + Task> PostMaybeUnitResponseAsync(string uri, TRequest body) + where TRequest : class; + Task> PostEitherUnitResponseAsync(string uri); Task> PostEitherUnitResponseAsync(string uri, TRequest body) diff --git a/Crypter.Common.Client/Interfaces/Requests/IUserConsentRequests.cs b/Crypter.Common.Client/Interfaces/Requests/IUserConsentRequests.cs index f22711826..32d87e344 100644 --- a/Crypter.Common.Client/Interfaces/Requests/IUserConsentRequests.cs +++ b/Crypter.Common.Client/Interfaces/Requests/IUserConsentRequests.cs @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Crypter File Transfer + * Copyright (C) 2025 Crypter File Transfer * * This file is part of the Crypter file transfer project. * @@ -24,12 +24,16 @@ * Contact the current copyright holder to discuss commercial license options. */ +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using Crypter.Common.Contracts.Features.UserConsents; using EasyMonads; namespace Crypter.Common.Client.Interfaces.Requests; public interface IUserConsentRequests { - Task> ConsentToRecoveryKeyRisksAsync(); + Task>> GetUserConsentsAsync(); + Task> ConsentAsync(UserConsentType consentType); } diff --git a/Crypter.Common.Client/Services/UserSessionService.cs b/Crypter.Common.Client/Services/UserSessionService.cs index 7038be77e..9a6269a1f 100644 --- a/Crypter.Common.Client/Services/UserSessionService.cs +++ b/Crypter.Common.Client/Services/UserSessionService.cs @@ -35,6 +35,7 @@ using Crypter.Common.Client.Interfaces.Services; using Crypter.Common.Client.Models; using Crypter.Common.Contracts.Features.UserAuthentication; +using Crypter.Common.Contracts.Features.UserConsents; using Crypter.Common.Enums; using Crypter.Common.Primitives; using EasyMonads; @@ -144,9 +145,16 @@ public Task> LoginAsync(Username username, Password pas from unit0 in Either.FromRightAsync(StoreSessionInfo(loginResponse, rememberUser)) select loginResponse; - Either loginResult = await loginTask; - loginResult.DoRight(x => HandleUserLoggedInEvent(username, password, versionedPassword, rememberUser, x.ShowRecoveryKey)); - return loginResult.Map(_ => Unit.Default); + return await loginTask + .DoRightAsync(async x => + { + bool showRecoveryKeyModal = await _crypterApiClient.UserConsent.GetUserConsentsAsync() + .MatchAsync( + none: () => false, + some: y => y.TryGetValue(UserConsentType.RecoveryKeyRisks, out DateTimeOffset? value) && !value.HasValue); + HandleUserLoggedInEvent(username, password, versionedPassword, rememberUser, showRecoveryKeyModal); + }) + .BindAsync(_ => Unit.Default); }); } diff --git a/Crypter.Common/Contracts/Features/UserAuthentication/Login/LoginResponse.cs b/Crypter.Common/Contracts/Features/UserAuthentication/Login/LoginResponse.cs index b9de26cd6..dc2601a39 100644 --- a/Crypter.Common/Contracts/Features/UserAuthentication/Login/LoginResponse.cs +++ b/Crypter.Common/Contracts/Features/UserAuthentication/Login/LoginResponse.cs @@ -33,14 +33,12 @@ public class LoginResponse public string Username { get; init; } public string AuthenticationToken { get; init; } public string RefreshToken { get; init; } - public bool ShowRecoveryKey { get; init; } [JsonConstructor] - public LoginResponse(string username, string authenticationToken, string refreshToken, bool showRecoveryKey) + public LoginResponse(string username, string authenticationToken, string refreshToken) { Username = username; AuthenticationToken = authenticationToken; RefreshToken = refreshToken; - ShowRecoveryKey = showRecoveryKey; } } diff --git a/Crypter.Common/Contracts/Features/UserConsents/UserConsentRequest.cs b/Crypter.Common/Contracts/Features/UserConsents/UserConsentRequest.cs new file mode 100644 index 000000000..c03f59eeb --- /dev/null +++ b/Crypter.Common/Contracts/Features/UserConsents/UserConsentRequest.cs @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2025 Crypter File Transfer + * + * This file is part of the Crypter file transfer project. + * + * Crypter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Crypter source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * You can be released from the requirements of the aforementioned license + * by purchasing a commercial license. Buying such a license is mandatory + * as soon as you develop commercial activities involving the Crypter source + * code without disclosing the source code of your own applications. + * + * Contact the current copyright holder to discuss commercial license options. + */ + +using System.Text.Json.Serialization; + +namespace Crypter.Common.Contracts.Features.UserConsents; + +public sealed record UserConsentRequest +{ + public UserConsentType ConsentType { get; init; } + + [JsonConstructor] + public UserConsentRequest(UserConsentType consentType) + { + ConsentType = consentType; + } +} diff --git a/Crypter.Common/Contracts/Features/UserConsents/UserConsentType.cs b/Crypter.Common/Contracts/Features/UserConsents/UserConsentType.cs new file mode 100644 index 000000000..2b08b7c2e --- /dev/null +++ b/Crypter.Common/Contracts/Features/UserConsents/UserConsentType.cs @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2025 Crypter File Transfer + * + * This file is part of the Crypter file transfer project. + * + * Crypter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Crypter source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * You can be released from the requirements of the aforementioned license + * by purchasing a commercial license. Buying such a license is mandatory + * as soon as you develop commercial activities involving the Crypter source + * code without disclosing the source code of your own applications. + * + * Contact the current copyright holder to discuss commercial license options. + */ + +namespace Crypter.Common.Contracts.Features.UserConsents; + +public enum UserConsentType +{ + + TermsOfService, + PrivacyPolicy, + RecoveryKeyRisks +} diff --git a/Crypter.Core/Features/AccountRecovery/Commands/SubmitAccountRecoveryCommand.cs b/Crypter.Core/Features/AccountRecovery/Commands/SubmitAccountRecoveryCommand.cs index be4fb5812..34353e7a0 100644 --- a/Crypter.Core/Features/AccountRecovery/Commands/SubmitAccountRecoveryCommand.cs +++ b/Crypter.Core/Features/AccountRecovery/Commands/SubmitAccountRecoveryCommand.cs @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Crypter File Transfer + * Copyright (C) 2025 Crypter File Transfer * * This file is part of the Crypter file transfer project. * @@ -29,6 +29,7 @@ using System.Threading; using System.Threading.Tasks; using Crypter.Common.Contracts.Features.AccountRecovery.SubmitRecovery; +using Crypter.Common.Contracts.Features.UserConsents; using Crypter.Common.Infrastructure; using Crypter.Common.Primitives; using Crypter.Core.Features.AccountRecovery.Events; @@ -163,8 +164,8 @@ await result.DoRightAsync(async recoveryResult => UserConsentEntity? latestRecoveryConsent = await _dataContext.UserConsents .Where(x => x.Owner == user.Id) - .Where(x => x.ConsentType == ConsentType.RecoveryKeyRisks) - .OrderBy(x => x.Created) + .Where(x => x.ConsentType == UserConsentType.RecoveryKeyRisks) + .OrderBy(x => x.Activated) .FirstOrDefaultAsync(); if (latestRecoveryConsent is not null) diff --git a/Crypter.Core/Features/UserAuthentication/Commands/UserLoginCommand.cs b/Crypter.Core/Features/UserAuthentication/Commands/UserLoginCommand.cs index 8e3bd6682..4d5dd88ad 100644 --- a/Crypter.Core/Features/UserAuthentication/Commands/UserLoginCommand.cs +++ b/Crypter.Core/Features/UserAuthentication/Commands/UserLoginCommand.cs @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Crypter File Transfer + * Copyright (C) 2025 Crypter File Transfer * * This file is part of the Crypter file transfer project. * @@ -76,7 +76,7 @@ public UserLoginCommandHandler( _tokenService = tokenService; _clientPasswordVersion = passwordSettings.Value.ClientVersion; - _refreshTokenProviderMap = new Dictionary>() + _refreshTokenProviderMap = new Dictionary> { { TokenType.Session, _tokenService.NewSessionToken }, { TokenType.Device, _tokenService.NewDeviceToken } @@ -143,7 +143,6 @@ private async Task> GetUserAsync(ValidLoginReques _foundUserEntity = await _dataContext.Users .Where(x => x.Username == validLoginRequest.Username.Value) .Include(x => x.FailedLoginAttempts) - .Include(x => x.Consents!.Where(y => y.Active == true)) .Include(x => x.MasterKey) .Include(x => x.KeyPair) .FirstOrDefaultAsync(); @@ -221,11 +220,8 @@ private async Task> CreateLoginResponseAsync(U string authToken = _tokenService.NewAuthenticationToken(userEntity.Id); await Common.PublishRefreshTokenCreatedEventAsync(_publisher, refreshToken); - - bool userHasConsentedToRecoveryKeyRisks = userEntity.Consents! - .Any(x => x.ConsentType == ConsentType.RecoveryKeyRisks); - - return new LoginResponse(userEntity.Username, authToken, refreshToken.Token, !userHasConsentedToRecoveryKeyRisks); + + return new LoginResponse(userEntity.Username, authToken, refreshToken.Token); } private Either> GetValidClientPasswords(List clientPasswords) diff --git a/Crypter.Core/Features/UserConsent/Commands/SaveAcknowledgementOfRecoveryKeyRisksCommand.cs b/Crypter.Core/Features/UserConsent/Commands/SaveAcknowledgementOfRecoveryKeyRisksCommand.cs deleted file mode 100644 index c2dfe3416..000000000 --- a/Crypter.Core/Features/UserConsent/Commands/SaveAcknowledgementOfRecoveryKeyRisksCommand.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Crypter.DataAccess; -using Crypter.DataAccess.Entities; -using EasyMonads; - -namespace Crypter.Core.Features.UserConsent.Commands; - -public record SaveAcknowledgementOfRecoveryKeyRisksCommand(Guid UserId) : MediatR.IRequest; - -// ReSharper disable once ClassNeverInstantiated.Global -internal sealed class SaveAcknowledgementOfRecoveryKeyRisksCommandHandler - : MediatR.IRequestHandler -{ - private readonly DataContext _dataContext; - - public SaveAcknowledgementOfRecoveryKeyRisksCommandHandler(DataContext dataContext) - { - _dataContext = dataContext; - } - - public async Task Handle(SaveAcknowledgementOfRecoveryKeyRisksCommand request, CancellationToken cancellationToken) - { - UserConsentEntity newConsent = - new UserConsentEntity(request.UserId, ConsentType.RecoveryKeyRisks, true, DateTime.UtcNow); - _dataContext.UserConsents.Add(newConsent); - await _dataContext.SaveChangesAsync(CancellationToken.None); - return Unit.Default; - } -} diff --git a/Crypter.Core/Features/UserConsent/Commands/SaveUserConsentCommand.cs b/Crypter.Core/Features/UserConsent/Commands/SaveUserConsentCommand.cs new file mode 100644 index 000000000..22bcacc93 --- /dev/null +++ b/Crypter.Core/Features/UserConsent/Commands/SaveUserConsentCommand.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Crypter.Common.Contracts.Features.UserConsents; +using Crypter.DataAccess; +using Crypter.DataAccess.Entities; +using MediatR; +using Unit = EasyMonads.Unit; + +namespace Crypter.Core.Features.UserConsent.Commands; + +public record SaveUserConsentCommand(Guid UserId, UserConsentType ConsentType) + : IRequest; + +internal sealed class SaveUserConsentCommandHandler + : IRequestHandler +{ + private readonly DataContext _dataContext; + + public SaveUserConsentCommandHandler(DataContext dataContext) + { + _dataContext = dataContext; + } + + public async Task Handle(SaveUserConsentCommand request, CancellationToken _) + { + UserConsentEntity newConsent = new UserConsentEntity(request.UserId, request.ConsentType, true, DateTime.UtcNow); + _dataContext.UserConsents.Add(newConsent); + await _dataContext.SaveChangesAsync(CancellationToken.None); + return Unit.Default; + } +} diff --git a/Crypter.Core/Features/UserConsent/Queries/GetUserConsentsQuery.cs b/Crypter.Core/Features/UserConsent/Queries/GetUserConsentsQuery.cs new file mode 100644 index 000000000..fd4177ee0 --- /dev/null +++ b/Crypter.Core/Features/UserConsent/Queries/GetUserConsentsQuery.cs @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2025 Crypter File Transfer + * + * This file is part of the Crypter file transfer project. + * + * Crypter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Crypter source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * You can be released from the requirements of the aforementioned license + * by purchasing a commercial license. Buying such a license is mandatory + * as soon as you develop commercial activities involving the Crypter source + * code without disclosing the source code of your own applications. + * + * Contact the current copyright holder to discuss commercial license options. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Crypter.Common.Contracts.Features.UserConsents; +using Crypter.DataAccess; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Crypter.Core.Features.UserConsent.Queries; + +public sealed record GetUserConsentsQuery(Guid UserId) + : IRequest>; + +internal sealed class GetUserConsentsQueryHandler + : IRequestHandler> +{ + private readonly DataContext _dataContext; + + public GetUserConsentsQueryHandler(DataContext dataContext) + { + _dataContext = dataContext; + } + + public async Task> Handle(GetUserConsentsQuery request, CancellationToken cancellationToken) + { + Dictionary activeConsents = await _dataContext.UserConsents + .Where(x => x.Owner == request.UserId && x.Active) + .ToDictionaryAsync(x => x.ConsentType, x => x.Activated, cancellationToken); + + Dictionary returnMap = Enum.GetValues() + .ToDictionary(x => x, _ => (DateTimeOffset?)null); + + foreach (KeyValuePair entry in returnMap) + { + if (activeConsents.TryGetValue(entry.Key, out DateTime consentTime)) + { + returnMap[entry.Key] = new DateTimeOffset(consentTime, TimeSpan.Zero); + } + } + + return returnMap; + } +} diff --git a/Crypter.DataAccess/Entities/UserConsentEntity.cs b/Crypter.DataAccess/Entities/UserConsentEntity.cs index 5eb6ccb44..6507d2d21 100644 --- a/Crypter.DataAccess/Entities/UserConsentEntity.cs +++ b/Crypter.DataAccess/Entities/UserConsentEntity.cs @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Crypter File Transfer + * Copyright (C) 2025 Crypter File Transfer * * This file is part of the Crypter file transfer project. * @@ -24,6 +24,7 @@ * Contact the current copyright holder to discuss commercial license options. */ +using Crypter.Common.Contracts.Features.UserConsents; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -43,31 +44,23 @@ public class UserConsentEntity { public long Id { get; set; } public Guid Owner { get; set; } - public ConsentType ConsentType { get; set; } - public DateTime Created { get; set; } + public UserConsentType ConsentType { get; set; } + public DateTime Activated { get; set; } public DateTime? Deactivated { get; set; } public bool Active { get; set; } public UserEntity? User { get; set; } - public UserConsentEntity(Guid owner, ConsentType consentType, bool active, DateTime created, - DateTime? deactivated = null) + public UserConsentEntity(Guid owner, UserConsentType consentType, bool active, DateTime activated, DateTime? deactivated = null) { Owner = owner; ConsentType = consentType; Active = active; - Created = created; + Activated = activated; Deactivated = deactivated; } } -public enum ConsentType -{ - TermsOfService, - PrivacyPolicy, - RecoveryKeyRisks -} - public class UserConsentEntityConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) diff --git a/Crypter.DataAccess/Migrations/20250114045437_RenameUserConsentColumn.Designer.cs b/Crypter.DataAccess/Migrations/20250114045437_RenameUserConsentColumn.Designer.cs new file mode 100644 index 000000000..26960f4ab --- /dev/null +++ b/Crypter.DataAccess/Migrations/20250114045437_RenameUserConsentColumn.Designer.cs @@ -0,0 +1,737 @@ +// +using System; +using System.Text.Json; +using Crypter.DataAccess; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Crypter.DataAccess.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250114045437_RenameUserConsentColumn")] + partial class RenameUserConsentColumn + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("crypter") + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Crypter.DataAccess.Entities.AnonymousFileTransferEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ContentType") + .IsRequired() + .HasColumnType("text"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("KeyExchangeNonce") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Parts") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Proof") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("AnonymousFileTransfer", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.AnonymousMessageTransferEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("KeyExchangeNonce") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Proof") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AnonymousMessageTransfer", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.EventLogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdditionalData") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("EventLogType") + .HasColumnType("integer"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EventLogType"); + + b.ToTable("EventLog", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserConsentEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); + + b.Property("Activated") + .HasColumnType("timestamp with time zone"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ConsentType") + .HasColumnType("integer"); + + b.Property("Deactivated") + .HasColumnType("timestamp with time zone"); + + b.Property("Owner") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("Owner"); + + b.ToTable("UserConsent", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserContactEntity", b => + { + b.Property("OwnerId") + .HasColumnType("uuid") + .HasColumnName("Owner"); + + b.Property("ContactId") + .HasColumnType("uuid") + .HasColumnName("Contact"); + + b.HasKey("OwnerId", "ContactId"); + + b.HasIndex("ContactId"); + + b.ToTable("UserContact", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserEmailVerificationEntity", b => + { + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("Code") + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("VerificationKey") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Owner"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("UserEmailVerification", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientPasswordVersion") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("EmailAddress") + .HasColumnType("citext"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("LastLogin") + .HasColumnType("timestamp with time zone"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("ServerPasswordVersion") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.Property("Username") + .IsRequired() + .HasColumnType("citext"); + + b.HasKey("Id"); + + b.HasIndex("EmailAddress") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("User", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserFailedLoginEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Owner") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("Owner"); + + b.ToTable("UserFailedLogin", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserFileTransferEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ContentType") + .IsRequired() + .HasColumnType("text"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("KeyExchangeNonce") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Parts") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Proof") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PublicKey") + .HasColumnType("bytea"); + + b.Property("RecipientId") + .HasColumnType("uuid") + .HasColumnName("Recipient"); + + b.Property("SenderId") + .HasColumnType("uuid") + .HasColumnName("Sender"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("UserFileTransfer", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserKeyPairEntity", b => + { + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Nonce") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PrivateKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Owner"); + + b.ToTable("UserKeyPair", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserMasterKeyEntity", b => + { + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Nonce") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("RecoveryProof") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Owner"); + + b.ToTable("UserMasterKey", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserMessageTransferEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("KeyExchangeNonce") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Proof") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PublicKey") + .HasColumnType("bytea"); + + b.Property("RecipientId") + .HasColumnType("uuid") + .HasColumnName("Recipient"); + + b.Property("SenderId") + .HasColumnType("uuid") + .HasColumnName("Sender"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("UserMessageTransfer", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserNotificationSettingEntity", b => + { + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("EmailNotifications") + .HasColumnType("boolean"); + + b.Property("EnableTransferNotifications") + .HasColumnType("boolean"); + + b.HasKey("Owner"); + + b.ToTable("UserNotificationSetting", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserPrivacySettingEntity", b => + { + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("AllowKeyExchangeRequests") + .HasColumnType("boolean"); + + b.Property("ReceiveFiles") + .HasColumnType("integer"); + + b.Property("ReceiveMessages") + .HasColumnType("integer"); + + b.Property("Visibility") + .HasColumnType("integer"); + + b.HasKey("Owner"); + + b.ToTable("UserPrivacySetting", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserProfileEntity", b => + { + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("About") + .IsRequired() + .HasColumnType("text"); + + b.Property("Alias") + .IsRequired() + .HasColumnType("text"); + + b.Property("Image") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Owner"); + + b.ToTable("UserProfile", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserRecoveryEntity", b => + { + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("Code") + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("VerificationKey") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Owner"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("UserRecovery", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserTokenEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("Owner") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Owner"); + + b.ToTable("UserToken", "crypter"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserConsentEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithMany("Consents") + .HasForeignKey("Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserContactEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "Contact") + .WithMany("Contactors") + .HasForeignKey("ContactId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "Owner") + .WithMany("Contacts") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Contact"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserEmailVerificationEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithOne("EmailVerification") + .HasForeignKey("Crypter.DataAccess.Entities.UserEmailVerificationEntity", "Owner") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserFailedLoginEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithMany("FailedLoginAttempts") + .HasForeignKey("Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserFileTransferEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "Recipient") + .WithMany("ReceivedFileTransfers") + .HasForeignKey("RecipientId"); + + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "Sender") + .WithMany("SentFileTransfers") + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserKeyPairEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithOne("KeyPair") + .HasForeignKey("Crypter.DataAccess.Entities.UserKeyPairEntity", "Owner") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserMasterKeyEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithOne("MasterKey") + .HasForeignKey("Crypter.DataAccess.Entities.UserMasterKeyEntity", "Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserMessageTransferEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "Recipient") + .WithMany("ReceivedMessageTransfers") + .HasForeignKey("RecipientId"); + + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "Sender") + .WithMany("SentMessageTransfers") + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserNotificationSettingEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithOne("NotificationSetting") + .HasForeignKey("Crypter.DataAccess.Entities.UserNotificationSettingEntity", "Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserPrivacySettingEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithOne("PrivacySetting") + .HasForeignKey("Crypter.DataAccess.Entities.UserPrivacySettingEntity", "Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserProfileEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithOne("Profile") + .HasForeignKey("Crypter.DataAccess.Entities.UserProfileEntity", "Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserRecoveryEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithOne("Recovery") + .HasForeignKey("Crypter.DataAccess.Entities.UserRecoveryEntity", "Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserTokenEntity", b => + { + b.HasOne("Crypter.DataAccess.Entities.UserEntity", "User") + .WithMany("Tokens") + .HasForeignKey("Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Crypter.DataAccess.Entities.UserEntity", b => + { + b.Navigation("Consents"); + + b.Navigation("Contactors"); + + b.Navigation("Contacts"); + + b.Navigation("EmailVerification"); + + b.Navigation("FailedLoginAttempts"); + + b.Navigation("KeyPair"); + + b.Navigation("MasterKey"); + + b.Navigation("NotificationSetting"); + + b.Navigation("PrivacySetting"); + + b.Navigation("Profile"); + + b.Navigation("ReceivedFileTransfers"); + + b.Navigation("ReceivedMessageTransfers"); + + b.Navigation("Recovery"); + + b.Navigation("SentFileTransfers"); + + b.Navigation("SentMessageTransfers"); + + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Crypter.DataAccess/Migrations/20250114045437_RenameUserConsentColumn.cs b/Crypter.DataAccess/Migrations/20250114045437_RenameUserConsentColumn.cs new file mode 100644 index 000000000..9c414a72f --- /dev/null +++ b/Crypter.DataAccess/Migrations/20250114045437_RenameUserConsentColumn.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Crypter.DataAccess.Migrations +{ + /// + public partial class RenameUserConsentColumn : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Created", + schema: "crypter", + table: "UserConsent", + newName: "Activated"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Activated", + schema: "crypter", + table: "UserConsent", + newName: "Created"); + } + } +} diff --git a/Crypter.DataAccess/Migrations/DataContextModelSnapshot.cs b/Crypter.DataAccess/Migrations/DataContextModelSnapshot.cs index 5987a3fad..384b34a7f 100644 --- a/Crypter.DataAccess/Migrations/DataContextModelSnapshot.cs +++ b/Crypter.DataAccess/Migrations/DataContextModelSnapshot.cs @@ -19,7 +19,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("crypter") - .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("ProductVersion", "9.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); @@ -137,15 +137,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); + b.Property("Activated") + .HasColumnType("timestamp with time zone"); + b.Property("Active") .HasColumnType("boolean"); b.Property("ConsentType") .HasColumnType("integer"); - b.Property("Created") - .HasColumnType("timestamp with time zone"); - b.Property("Deactivated") .HasColumnType("timestamp with time zone"); diff --git a/Crypter.Test/Integration_Tests/UserConsent_Tests/RecoveryKeyRisksConsent_Tests.cs b/Crypter.Test/Integration_Tests/UserConsent_Tests/RecoveryKeyRisksConsent_Tests.cs index d41d20622..5a97c2b8a 100644 --- a/Crypter.Test/Integration_Tests/UserConsent_Tests/RecoveryKeyRisksConsent_Tests.cs +++ b/Crypter.Test/Integration_Tests/UserConsent_Tests/RecoveryKeyRisksConsent_Tests.cs @@ -28,6 +28,7 @@ using Crypter.Common.Client.Interfaces.HttpClients; using Crypter.Common.Client.Interfaces.Repositories; using Crypter.Common.Contracts.Features.UserAuthentication; +using Crypter.Common.Contracts.Features.UserConsents; using Crypter.Common.Enums; using EasyMonads; using Microsoft.AspNetCore.Mvc.Testing; @@ -63,8 +64,7 @@ public async Task TeardownTestAsync() [Test] public async Task Consent_To_Recovery_Key_Risks_Works_Async() { - RegistrationRequest registrationRequest = - TestData.GetRegistrationRequest(TestData.DefaultUsername, TestData.DefaultPassword); + RegistrationRequest registrationRequest = TestData.GetRegistrationRequest(TestData.DefaultUsername, TestData.DefaultPassword); Either registrationResult = await _client!.UserAuthentication.RegisterAsync(registrationRequest); LoginRequest loginRequest = TestData.GetLoginRequest(TestData.DefaultUsername, TestData.DefaultPassword); @@ -76,7 +76,7 @@ await loginResult.DoRightAsync(async loginResponse => await _clientTokenRepository!.StoreRefreshTokenAsync(loginResponse.RefreshToken, TokenType.Session); }); - Maybe result = await _client!.UserConsent.ConsentToRecoveryKeyRisksAsync(); + Maybe result = await _client!.UserConsent.ConsentAsync(UserConsentType.RecoveryKeyRisks); Assert.That(registrationResult.IsRight, Is.True); Assert.That(loginResult.IsRight, Is.True); diff --git a/Crypter.Web/Shared/Modal/RecoveryKeyModal.razor.cs b/Crypter.Web/Shared/Modal/RecoveryKeyModal.razor.cs index f60e1e1c7..02547e4fc 100644 --- a/Crypter.Web/Shared/Modal/RecoveryKeyModal.razor.cs +++ b/Crypter.Web/Shared/Modal/RecoveryKeyModal.razor.cs @@ -28,6 +28,7 @@ using Crypter.Common.Client.Interfaces.HttpClients; using Crypter.Common.Client.Interfaces.Services; using Crypter.Common.Client.Models; +using Crypter.Common.Contracts.Features.UserConsents; using Crypter.Web.Shared.Modal.Template; using EasyMonads; using Microsoft.AspNetCore.Components; @@ -65,7 +66,7 @@ private async Task CopyRecoveryKeyToClipboardAsync() private async Task OnAcknowledgedClickedAsync() { - await CrypterApiService.UserConsent.ConsentToRecoveryKeyRisksAsync(); + await CrypterApiService.UserConsent.ConsentAsync(UserConsentType.RecoveryKeyRisks); _modalBehaviorRef.Close(); } }