Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented delete authenticator #122

Merged
merged 8 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
51 changes: 42 additions & 9 deletions src/PinguApps.Appwrite.Shared/AppwriteResult.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,73 @@
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()
{

Check notice on line 17 in src/PinguApps.Appwrite.Shared/AppwriteResult.cs

View check run for this annotation

codefactor.io / CodeFactor

src/PinguApps.Appwrite.Shared/AppwriteResult.cs#L17

An opening brace should not be followed by a blank line. (SA1505)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A closing brace should not be preceded by a blank line.

Suggested change

}

Check notice on line 19 in src/PinguApps.Appwrite.Shared/AppwriteResult.cs

View check run for this annotation

codefactor.io / CodeFactor

src/PinguApps.Appwrite.Shared/AppwriteResult.cs#L19

A closing brace should not be preceded by a blank line. (SA1508)

/// <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;
}


/// <inheritdoc/>
/// <typeparam name="TResult">the type of response expected on success</typeparam>
public class AppwriteResult<TResult> : AppwriteResult

Check notice on line 50 in src/PinguApps.Appwrite.Shared/AppwriteResult.cs

View check run for this annotation

codefactor.io / CodeFactor

src/PinguApps.Appwrite.Shared/AppwriteResult.cs#L50

File may only contain a single type. (SA1402)
{
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