From 03625e2d62b0e70f85bc36180e1deb07b7085f16 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 8 Aug 2024 23:40:41 +0100 Subject: [PATCH 1/5] Implemented update mfa --- .../Clients/AccountClient.cs | 17 +++++++++++++++++ .../Clients/IAccountClient.cs | 7 +++++++ .../Internals/IAccountApi.cs | 3 +++ src/PinguApps.Appwrite.Playground/App.cs | 4 ++-- .../Requests/UpdateMfaRequest.cs | 16 ++++++++++++++++ .../Validators/UpdateMfaRequestValidator.cs | 10 ++++++++++ 6 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/UpdateMfaRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Validators/UpdateMfaRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs index cb355951..9a6ef10c 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs @@ -329,4 +329,21 @@ public async Task> VerifyAuthenticator(VerifyAuthenticatorR return e.GetExceptionResponse(); } } + + /// + public async Task> UpdateMfa(UpdateMfaRequest request) + { + try + { + request.Validate(true); + + var result = await _accountApi.UpdateMfa(Session, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } } diff --git a/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs index ab244046..13f01319 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs @@ -157,4 +157,11 @@ public interface IAccountClient /// Type of authenticator /// The User Task> VerifyAuthenticator(VerifyAuthenticatorRequest request, string type = "totp"); + + /// + /// Enable or disable MFA on an account + /// + /// The request content + /// The user + Task> UpdateMfa(UpdateMfaRequest request); } diff --git a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs index d3aac654..d4533158 100644 --- a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs +++ b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs @@ -62,4 +62,7 @@ internal interface IAccountApi : IBaseApi [Put("/account/mfa/authenticators/{type}")] Task> VerifyAuthenticator([Header("x-appwrite-session")] string? session, string type, VerifyAuthenticatorRequest request); + + [Patch("/account/mfa")] + Task> UpdateMfa([Header("x-appwrite-session")] string? session, UpdateMfaRequest request); } diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 49b95ab2..41e9c111 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -21,9 +21,9 @@ public async Task Run(string[] args) _client.SetSession(_session); //var response = await _client.Account.AddAuthenticator(); - var response = await _client.Account.VerifyAuthenticator(new Shared.Requests.VerifyAuthenticatorRequest + var response = await _client.Account.UpdateMfa(new Shared.Requests.UpdateMfaRequest { - Otp = "623850" + MfaEnabled = false }); Console.WriteLine(response.Result.Match( diff --git a/src/PinguApps.Appwrite.Shared/Requests/UpdateMfaRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/UpdateMfaRequest.cs new file mode 100644 index 00000000..3290ecc8 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/UpdateMfaRequest.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Validators; + +namespace PinguApps.Appwrite.Shared.Requests; + +/// +/// The request for updating Mfa +/// +public class UpdateMfaRequest : BaseRequest +{ + /// + /// Enable or disable MFA + /// + [JsonPropertyName("mfa")] + public bool MfaEnabled { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdateMfaRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdateMfaRequestValidator.cs new file mode 100644 index 00000000..8cacd018 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdateMfaRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Validators; +public class UpdateMfaRequestValidator : AbstractValidator +{ + public UpdateMfaRequestValidator() + { + RuleFor(x => x.MfaEnabled).NotNull(); + } +} From 79c73077106708e252db67ba20c97d5bfd60ae87 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 8 Aug 2024 23:46:07 +0100 Subject: [PATCH 2/5] added shared tests --- .../Requests/UpdateMfaRequestTests.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs new file mode 100644 index 00000000..a31d062b --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs @@ -0,0 +1,47 @@ +using PinguApps.Appwrite.Shared.Requests; + +namespace PinguApps.Appwrite.Shared.Tests.Requests; +public class UpdateMfaRequestTests +{ + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateMfaRequest(); + + // Assert + Assert.False(request.MfaEnabled); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var request = new UpdateMfaRequest(); + + // Act + request.MfaEnabled = true; + + // Assert + Assert.True(request.MfaEnabled); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void IsValid_WithValidData_ReturnsTrue(bool mfaEnabled) + { + // Arrange + var request = new UpdateMfaRequest + { + MfaEnabled = mfaEnabled + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } +} From 615550a00939a9c589973d5ceb4e68c0485e4faa Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 8 Aug 2024 23:48:44 +0100 Subject: [PATCH 3/5] added client tests --- .../Account/AccountClientTests.UpdateMfa.cs | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdateMfa.cs diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdateMfa.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdateMfa.cs new file mode 100644 index 00000000..885abe12 --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdateMfa.cs @@ -0,0 +1,80 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests; +using PinguApps.Appwrite.Shared.Tests; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Account; +public partial class AccountClientTests +{ + [Fact] + public async Task UpdateMfa_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateMfaRequest() + { + MfaEnabled = true + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/mfa") + .ExpectedHeaders(true) + .WithJsonContent(request) + .Respond(Constants.AppJson, Constants.UserResponse); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.UpdateMfa(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateMfa_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateMfaRequest() + { + MfaEnabled = true + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/mfa") + .ExpectedHeaders(true) + .WithJsonContent(request) + .Respond(HttpStatusCode.BadRequest, Constants.AppJson, Constants.AppwriteError); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.UpdateMfa(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateMfa_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateMfaRequest() + { + MfaEnabled = true + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/mfa") + .ExpectedHeaders(true) + .WithJsonContent(request) + .Throw(new HttpRequestException("An error occurred")); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.UpdateMfa(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 456bd249bb950e81eec3dc00807552d3f5f4a122 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 8 Aug 2024 23:50:06 +0100 Subject: [PATCH 4/5] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 67fbfa5b..5a675546 100644 --- a/README.md +++ b/README.md @@ -138,14 +138,14 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ``` ## ⌛ Progress - -![Server & Client - 20 / 288](https://img.shields.io/badge/Server_&_Client-20%20%2F%20288-red?style=for-the-badge) + +![Server & Client - 21 / 288](https://img.shields.io/badge/Server_&_Client-21%20%2F%20288-red?style=for-the-badge) ![Server - 2 / 195](https://img.shields.io/badge/Server-2%20%2F%20195-red?style=for-the-badge) - -![Client - 18 / 93](https://img.shields.io/badge/Client-18%20%2F%2093-red?style=for-the-badge) + +![Client - 19 / 93](https://img.shields.io/badge/Client-19%20%2F%2093-red?style=for-the-badge) ### 🔑 Key | Icon | Definition | @@ -155,8 +155,8 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | ❌ | There is currently no intention to implement the endpoint for the given SDK type (client or server) | ### Account - -![Account - 20 / 52](https://img.shields.io/badge/Account-20%20%2F%2052-yellow?style=for-the-badge) + +![Account - 21 / 52](https://img.shields.io/badge/Account-21%20%2F%2052-yellow?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -167,7 +167,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Delete Identity](https://appwrite.io/docs/references/1.5.x/client-rest/account#deleteIdentity) | ⬛ | ❌ | | [Create JWT](https://appwrite.io/docs/references/1.5.x/client-rest/account#createJWT) | ✅ | ❌ | | [List Logs](https://appwrite.io/docs/references/1.5.x/client-rest/account#listLogs) | ✅ | ❌ | -| [Update MFA](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateMFA) | ⬛ | ❌ | +| [Update MFA](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateMFA) | ✅ | ❌ | | [Add Authenticator](https://appwrite.io/docs/references/1.5.x/client-rest/account#createMfaAuthenticator) | ✅ | ❌ | | [Verify Authenticator](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateMfaAuthenticator) | ✅ | ❌ | | [Delete Authenticator](https://appwrite.io/docs/references/1.5.x/client-rest/account#deleteMfaAuthenticator) | ⬛ | ❌ | From e2ffa3391b4222f4283dfa0ac7baa31975e8b66f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 8 Aug 2024 23:51:07 +0100 Subject: [PATCH 5/5] Update tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> --- .../Requests/UpdateMfaRequestTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs index a31d062b..ce37cb0c 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdateMfaRequestTests.cs @@ -3,7 +3,6 @@ namespace PinguApps.Appwrite.Shared.Tests.Requests; public class UpdateMfaRequestTests { - [Fact] public void Constructor_InitializesWithExpectedValues() {