Skip to content

Commit

Permalink
Merge pull request #122 from PinguApps/89-delete-authenticator
Browse files Browse the repository at this point in the history
Implemented delete authenticator
  • Loading branch information
pingu2k4 authored Aug 9, 2024
2 parents 78e0aaf + 344d026 commit df9813f
Show file tree
Hide file tree
Showing 24 changed files with 729 additions and 42 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,14 @@ string emailAddressOrErrorMessage = userResponse.Result.Match(
```

## ⌛ Progress
<!-- ![21 / 288](https://progress-bar.dev/21/?scale=288&suffix=%20/%20288&width=500) -->
![Server & Client - 21 / 288](https://img.shields.io/badge/Server_&_Client-21%20%2F%20288-red?style=for-the-badge)
<!-- ![22 / 288](https://progress-bar.dev/22/?scale=288&suffix=%20/%20288&width=500) -->
![Server & Client - 22 / 288](https://img.shields.io/badge/Server_&_Client-22%20%2F%20288-red?style=for-the-badge)

<!-- ![2 / 195](https://progress-bar.dev/2/?scale=195&suffix=%20/%20195&width=300) -->
![Server - 2 / 195](https://img.shields.io/badge/Server-2%20%2F%20195-red?style=for-the-badge)

<!-- ![19 / 93](https://progress-bar.dev/19/?scale=93&suffix=%20/%2093&width=300) -->
![Client - 19 / 93](https://img.shields.io/badge/Client-19%20%2F%2093-red?style=for-the-badge)
<!-- ![20 / 93](https://progress-bar.dev/20/?scale=93&suffix=%20/%2093&width=300) -->
![Client - 20 / 93](https://img.shields.io/badge/Client-20%20%2F%2093-red?style=for-the-badge)

### 🔑 Key
| Icon | Definition |
Expand All @@ -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
<!-- ![21 / 52](https://progress-bar.dev/21/?scale=52&suffix=%20/%2052&width=120) -->
![Account - 21 / 52](https://img.shields.io/badge/Account-21%20%2F%2052-yellow?style=for-the-badge)
<!-- ![22 / 52](https://progress-bar.dev/22/?scale=52&suffix=%20/%2052&width=120) -->
![Account - 22 / 52](https://img.shields.io/badge/Account-22%20%2F%2052-yellow?style=for-the-badge)

| Endpoint | Client | Server |
|:-:|:-:|:-:|
Expand All @@ -170,7 +170,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match(
| [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) | ||
| [Delete Authenticator](https://appwrite.io/docs/references/1.5.x/client-rest/account#deleteMfaAuthenticator) | ||
| [Create 2FA Challenge](https://appwrite.io/docs/references/1.5.x/client-rest/account#createMfaChallenge) |||
| [Create MFA Challenge (confirmation)](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateMfaChallenge) |||
| [List Factors](https://appwrite.io/docs/references/1.5.x/client-rest/account#listMfaFactors) |||
Expand Down
27 changes: 23 additions & 4 deletions src/PinguApps.Appwrite.Client/Clients/AccountClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,13 @@ public async Task<AppwriteResult<LogsList>> ListLogs(List<Query>? queries = null
}

/// <inheritdoc/>
public async Task<AppwriteResult<MfaType>> AddAuthenticator(string type = "totp")
public async Task<AppwriteResult<MfaType>> AddAuthenticator(AddAuthenticatorRequest request)
{
try
{
var result = await _accountApi.AddAuthenticator(Session, type);
request.Validate(true);

var result = await _accountApi.AddAuthenticator(Session, request.Type);

return result.GetApiResponse();
}
Expand All @@ -314,13 +316,13 @@ public async Task<AppwriteResult<MfaType>> AddAuthenticator(string type = "totp"
}

/// <inheritdoc/>
public async Task<AppwriteResult<User>> VerifyAuthenticator(VerifyAuthenticatorRequest request, string type = "totp")
public async Task<AppwriteResult<User>> VerifyAuthenticator(VerifyAuthenticatorRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.VerifyAuthenticator(Session, type, request);
var result = await _accountApi.VerifyAuthenticator(Session, request.Type, request);

return result.GetApiResponse();
}
Expand All @@ -346,4 +348,21 @@ public async Task<AppwriteResult<User>> UpdateMfa(UpdateMfaRequest request)
return e.GetExceptionResponse<User>();
}
}

/// <inheritdoc/>
public async Task<AppwriteResult> DeleteAuthenticator(DeleteAuthenticatorRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.DeleteAuthenticator(Session, request.Type, request);

return result.GetApiResponse();
}
catch (Exception e)
{
return e.GetExceptionResponse();
}
}
}
12 changes: 10 additions & 2 deletions src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public interface IAccountClient
/// </summary>
/// <param name="type">Type of authenticator. Must be `totp`</param>
/// <returns>The MfaType</returns>
Task<AppwriteResult<MfaType>> AddAuthenticator(string type = "totp");
Task<AppwriteResult<MfaType>> AddAuthenticator(AddAuthenticatorRequest request);

/// <summary>
/// Verify an authenticator app after adding it using <see cref="AddAuthenticator"/>.
Expand All @@ -157,7 +157,7 @@ public interface IAccountClient
/// <param name="request">The request content</param>
/// <param name="type">Type of authenticator</param>
/// <returns>The User</returns>
Task<AppwriteResult<User>> VerifyAuthenticator(VerifyAuthenticatorRequest request, string type = "totp");
Task<AppwriteResult<User>> VerifyAuthenticator(VerifyAuthenticatorRequest request);

/// <summary>
/// Enable or disable MFA on an account
Expand All @@ -166,4 +166,12 @@ public interface IAccountClient
/// <param name="request">The request content</param>
/// <returns>The user</returns>
Task<AppwriteResult<User>> UpdateMfa(UpdateMfaRequest request);

/// <summary>
/// Delete an authenticator for a user by ID
/// <para><see href="https://appwrite.io/docs/references/1.5.x/client-rest/account#deleteMfaAuthenticator">Appwrite Docs</see></para>
/// </summary>
/// <param name="request">The request content</param>
/// <returns>The result</returns>
Task<AppwriteResult> DeleteAuthenticator(DeleteAuthenticatorRequest request);
}
3 changes: 3 additions & 0 deletions src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@ internal interface IAccountApi : IBaseApi

[Patch("/account/mfa")]
Task<IApiResponse<User>> UpdateMfa([Header("x-appwrite-session")] string? session, UpdateMfaRequest request);

[Delete("/account/mfa/authenticators/{type}")]
Task<IApiResponse> DeleteAuthenticator([Header("x-appwrite-session")] string? session, string type, [Body] DeleteAuthenticatorRequest request);
}
23 changes: 23 additions & 0 deletions src/PinguApps.Appwrite.Client/Utils/ResponseUtils.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
using System;
using System.Text.Json;
using OneOf.Types;
using PinguApps.Appwrite.Shared;
using Refit;

namespace PinguApps.Appwrite.Client.Utils;
internal static class ResponseUtils
{
internal static AppwriteResult GetApiResponse(this IApiResponse result)
{
if (result.IsSuccessStatusCode)
{
return new AppwriteResult(new Success());
}

if (result.Error?.Content is null || string.IsNullOrEmpty(result.Error.Content))
{
throw new Exception("Unknown error encountered.");
}

var error = JsonSerializer.Deserialize<AppwriteError>(result.Error.Content);

return new AppwriteResult(error!);
}

internal static AppwriteResult<T> GetApiResponse<T>(this IApiResponse<T> result)
{
if (result.IsSuccessStatusCode)
Expand All @@ -28,6 +46,11 @@ internal static AppwriteResult<T> GetApiResponse<T>(this IApiResponse<T> result)
return new AppwriteResult<T>(error!);
}

internal static AppwriteResult GetExceptionResponse(this Exception e)
{
return new AppwriteResult(new InternalError(e.Message));
}

internal static AppwriteResult<T> GetExceptionResponse<T>(this Exception e)
{
return new AppwriteResult<T>(new InternalError(e.Message));
Expand Down
5 changes: 3 additions & 2 deletions src/PinguApps.Appwrite.Playground/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ public async Task Run(string[] args)
_client.SetSession(_session);

//var response = await _client.Account.AddAuthenticator();
var response = await _client.Account.UpdateMfa(new Shared.Requests.UpdateMfaRequest
var response = await _client.Account.DeleteAuthenticator(new Shared.Requests.DeleteAuthenticatorRequest
{
MfaEnabled = false
Type = "totp",
Otp = "413526"
});

Console.WriteLine(response.Result.Match(
Expand Down
23 changes: 23 additions & 0 deletions src/PinguApps.Appwrite.Server/Utils/ResponseUtils.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
using System;
using System.Text.Json;
using OneOf.Types;
using PinguApps.Appwrite.Shared;
using Refit;

namespace PinguApps.Appwrite.Server.Utils;
internal static class ResponseUtils
{
internal static AppwriteResult GetApiResponse(this IApiResponse result)
{
if (result.IsSuccessStatusCode)
{
return new AppwriteResult(new Success());
}

if (result.Error?.Content is null || string.IsNullOrEmpty(result.Error.Content))
{
throw new Exception("Unknown error encountered.");
}

var error = JsonSerializer.Deserialize<AppwriteError>(result.Error.Content);

return new AppwriteResult(error!);
}

internal static AppwriteResult<T> GetApiResponse<T>(this IApiResponse<T> result)
{
if (result.IsSuccessStatusCode)
Expand All @@ -28,6 +46,11 @@ internal static AppwriteResult<T> GetApiResponse<T>(this IApiResponse<T> result)
return new AppwriteResult<T>(error!);
}

internal static AppwriteResult GetExceptionResponse(this Exception e)
{
return new AppwriteResult(new InternalError(e.Message));
}

internal static AppwriteResult<T> GetExceptionResponse<T>(this Exception e)
{
return new AppwriteResult<T>(new InternalError(e.Message));
Expand Down
22 changes: 13 additions & 9 deletions src/PinguApps.Appwrite.Shared/AppwriteResult.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
using OneOf;
using OneOf.Types;

namespace PinguApps.Appwrite.Shared;

/// <summary>
/// The result of all API calls
/// </summary>
/// <typeparam name="TResult">the type of response expected on success</typeparam>
public class AppwriteResult<TResult>
public class AppwriteResult
{
public AppwriteResult(OneOf<TResult, AppwriteError, InternalError> result)
public AppwriteResult(OneOf<Success, AppwriteError, InternalError> result)
{
Result = result;
}

protected AppwriteResult()
{
}

/// <summary>
/// The result of making the API call. Can be <see cref="TResult"/>, <see cref="AppwriteError"/> or <see cref="InternalError"/> depending on what happened
/// The result of making the API call. Can be <see cref="OneOf.Types.Success"/>, <see cref="AppwriteError"/> or <see cref="InternalError"/> depending on what happened
/// </summary>
public OneOf<TResult, AppwriteError, InternalError> Result { get; }
public OneOf<Success, AppwriteError, InternalError> Result { get; }

/// <summary>
/// Indicates the API call was successful
/// </summary>
public bool Success => Result.IsT0;
public virtual bool Success => Result.IsT0;

/// <summary>
/// Indicates there is an error
/// </summary>
public bool IsError => Result.IsT1 || Result.IsT2;
public virtual bool IsError => Result.IsT1 || Result.IsT2;

/// <summary>
/// Indicates that there was an error thrown within Appwrite
/// </summary>
public bool IsAppwriteError => Result.IsT1;
public virtual bool IsAppwriteError => Result.IsT1;

/// <summary>
/// Indicates that there was an error thrown within the SDK
/// </summary>
public bool IsInternalError => Result.IsT2;
public virtual bool IsInternalError => Result.IsT2;
}
30 changes: 30 additions & 0 deletions src/PinguApps.Appwrite.Shared/AppwriteResultOfT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using OneOf;

namespace PinguApps.Appwrite.Shared;

/// <inheritdoc/>
/// <typeparam name="TResult">the type of response expected on success</typeparam>
public class AppwriteResult<TResult> : AppwriteResult
{
public AppwriteResult(OneOf<TResult, AppwriteError, InternalError> result)
{
Result = result;
}

/// <summary>
/// /// The result of making the API call. Can be <see cref="TResult"/>, <see cref="AppwriteError"/> or <see cref="InternalError"/> depending on what happened
/// </summary>
public new OneOf<TResult, AppwriteError, InternalError> Result { get; }

/// <inheritdoc/>
public override bool Success => Result.IsT0;

/// <inheritdoc/>
public override bool IsError => Result.IsT1 || Result.IsT2;

/// <inheritdoc/>
public override bool IsAppwriteError => Result.IsT1;

/// <inheritdoc/>
public override bool IsInternalError => Result.IsT2;
}
12 changes: 12 additions & 0 deletions src/PinguApps.Appwrite.Shared/Requests/AddAuthenticatorRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;
public class AddAuthenticatorRequest : BaseRequest<AddAuthenticatorRequest, AddAuthenticatorRequestValidator>
{
/// <summary>
/// Type of authenticator. Must be `totp`
/// </summary>
[JsonIgnore]
public string Type { get; set; } = "totp";
}
9 changes: 9 additions & 0 deletions src/PinguApps.Appwrite.Shared/Requests/BaseRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ public abstract class BaseRequest<TRequest, TValidator>
where TRequest : class
where TValidator : IValidator<TRequest>, new()
{
/// <summary>
/// True if the request object passes all validation
/// </summary>
/// <returns>Whether the request object is valid</returns>
public bool IsValid() => Validate().IsValid;

/// <summary>
/// Attempts to validate the request object
/// </summary>
/// <param name="throwOnFailures">If true, throws an exception on failure</param>
/// <returns>The result, showing any errors if applicable</returns>
public ValidationResult Validate(bool throwOnFailures = false)
{
var validator = new TValidator();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;

/// <summary>
/// The request for deleting an authenticator
/// </summary>
public class DeleteAuthenticatorRequest : BaseRequest<DeleteAuthenticatorRequest, DeleteAuthenticatorRequestValidator>
{
/// <summary>
/// Type of authenticator
/// </summary>
[JsonIgnore]
public string Type { get; set; } = "totp";

/// <summary>
/// Valid verification token
/// </summary>
[JsonPropertyName("otp")]
public string Otp { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class AddAuthenticatorRequestValidator : AbstractValidator<AddAuthenticatorRequest>
{
public AddAuthenticatorRequestValidator()
{
RuleFor(x => x.Type).NotEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class DeleteAuthenticatorRequestValidator : AbstractValidator<DeleteAuthenticatorRequest>
{
public DeleteAuthenticatorRequestValidator()
{
RuleFor(x => x.Type).NotEmpty();
RuleFor(x => x.Otp).NotEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public class VerifyAuthenticatorRequestValidator : AbstractValidator<VerifyAuthe
public VerifyAuthenticatorRequestValidator()
{
RuleFor(x => x.Otp).NotEmpty();
RuleFor(x => x.Type).NotEmpty();
}
}
Loading

0 comments on commit df9813f

Please sign in to comment.