Skip to content

Commit

Permalink
feat: add possibility to query whether an identity was deleted
Browse files Browse the repository at this point in the history
  • Loading branch information
tnotheis committed Nov 26, 2024
1 parent 14ee5c3 commit 3fd84b1
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Backbone.DevelopmentKit.Identity.ValueObjects;
using Backbone.Modules.Devices.Application.Infrastructure.Persistence.Repository;
using Backbone.Modules.Devices.Domain.Entities.Identities;
using MediatR;

namespace Backbone.Modules.Devices.Application.Identities.Queries.IsIdentityOfUserDeleted;

public class Handler : IRequestHandler<IsIdentityOfUserDeletedQuery, IsIdentityOfUserDeletedResponse>
{
private readonly IIdentitiesRepository _identitiesRepository;

public Handler(IIdentitiesRepository identitiesRepository)
{
_identitiesRepository = identitiesRepository;
}

public async Task<IsIdentityOfUserDeletedResponse> Handle(IsIdentityOfUserDeletedQuery request, CancellationToken cancellationToken)
{
var auditLogEntries = await _identitiesRepository.GetIdentityDeletionProcessAuditLogs(
IdentityDeletionProcessAuditLogEntry.BelongsToUser(Username.Parse(request.Username)),
cancellationToken);

var deletionCompletedAuditLogEntry = auditLogEntries.FirstOrDefault(l => l.MessageKey == MessageKey.DeletionCompleted);

return new IsIdentityOfUserDeletedResponse(deletionCompletedAuditLogEntry != null, deletionCompletedAuditLogEntry?.CreatedAt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using MediatR;

namespace Backbone.Modules.Devices.Application.Identities.Queries.IsIdentityOfUserDeleted;

public class IsIdentityOfUserDeletedQuery : IRequest<IsIdentityOfUserDeletedResponse>
{
public IsIdentityOfUserDeletedQuery(string username)
{
Username = username;
}

public string Username { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Backbone.Modules.Devices.Application.Identities.Queries.IsIdentityOfUserDeleted;

public class IsIdentityOfUserDeletedResponse
{
public IsIdentityOfUserDeletedResponse(bool isDeleted, DateTime? deletionDate)
{
IsDeleted = isDeleted;
DeletionDate = deletionDate;
}

public bool IsDeleted { get; set; }
public DateTime? DeletionDate { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Backbone.BuildingBlocks.Application.Extensions;
using Backbone.DevelopmentKit.Identity.ValueObjects;
using FluentValidation;

namespace Backbone.Modules.Devices.Application.Identities.Queries.IsIdentityOfUserDeleted;

public class Validator : AbstractValidator<IsIdentityOfUserDeletedQuery>
{
public Validator()
{
RuleFor(x => x.Username).ValidId<IsIdentityOfUserDeletedQuery, Username>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Backbone.Modules.Devices.Application.Identities.Queries.GetDeletionProcessAsOwner;
using Backbone.Modules.Devices.Application.Identities.Queries.GetDeletionProcessesAsOwner;
using Backbone.Modules.Devices.Application.Identities.Queries.GetOwnIdentity;
using Backbone.Modules.Devices.Application.Identities.Queries.IsIdentityOfUserDeleted;
using Backbone.Modules.Devices.Infrastructure.OpenIddict;
using MediatR;
using Microsoft.AspNetCore.Authorization;
Expand Down Expand Up @@ -128,6 +129,16 @@ public async Task<IActionResult> GetOwnIdentity(CancellationToken cancellationTo
var response = await _mediator.Send(new GetOwnIdentityQuery(), cancellationToken);
return Ok(response);
}

[HttpGet("IsDeleted")]
[ProducesResponseType(typeof(IsIdentityOfUserDeletedResponse), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status200OK)]
[AllowAnonymous]
public async Task<IActionResult> IsIdentityOfUserDeleted([FromQuery(Name = "username")] string username, CancellationToken cancellationToken)
{
var response = await _mediator.Send(new IsIdentityOfUserDeletedQuery(username), cancellationToken);
return Ok(response);
}
}

public class CreateIdentityRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Backbone.BuildingBlocks.Domain;
using System.Linq.Expressions;
using Backbone.BuildingBlocks.Domain;
using Backbone.DevelopmentKit.Identity.ValueObjects;
using Backbone.Tooling;

Expand Down Expand Up @@ -36,7 +37,7 @@ private IdentityDeletionProcessAuditLogEntry(IdentityDeletionProcessId? processI
public byte[] IdentityAddressHash { get; }
public byte[]? DeviceIdHash { get; }
public DeletionProcessStatus? OldStatus { get; }
public DeletionProcessStatus NewStatus { get; }
public DeletionProcessStatus? NewStatus { get; }
public Dictionary<string, string>? AdditionalData { get; }
public string? UsernameHashesBase64 { get; private set; }

Expand Down Expand Up @@ -231,6 +232,11 @@ public void AssociateUsernames(IEnumerable<Username> usernames)

UsernameHashesBase64 = concatenatedHashedUsernamesInBase64;
}

public static Expression<Func<IdentityDeletionProcessAuditLogEntry, bool>> BelongsToUser(Username username)
{
return logEntry => logEntry.UsernameHashesBase64!.Contains(Convert.ToBase64String(Hasher.HashUtf8(username.Value.Trim())));
}
}

public enum MessageKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,37 @@ public void AssociateUsernames_happy_path()
// Assert
auditLogEntry.UsernameHashesBase64.Should().NotBeEmpty();
}

[Fact]
public void BelongsToUser_returns_true_if_audit_log_entry_contains_username()
{
// Arrange
var username1 = Username.Parse("USR1111111111111111");
var username2 = Username.Parse("USR2222222222222222");
var auditLogEntry = IdentityDeletionProcessAuditLogEntry.DeletionCompleted(CreateRandomIdentityAddress());
auditLogEntry.AssociateUsernames([username1, username2]);

// Act
var resultForUsername1 = IdentityDeletionProcessAuditLogEntry.BelongsToUser(username1).Compile()(auditLogEntry);
var resultForUsername2 = IdentityDeletionProcessAuditLogEntry.BelongsToUser(username2).Compile()(auditLogEntry);

// Assert
resultForUsername1.Should().BeTrue();
resultForUsername2.Should().BeTrue();
}

[Fact]
public void BelongsToUser_returns_false_if_user_is_not_authorized()
{
// Arrange
var unauthorizedUsername = Username.Parse("USR3333333333333333");
var auditLogEntry = IdentityDeletionProcessAuditLogEntry.DeletionCompleted(CreateRandomIdentityAddress());
auditLogEntry.AssociateUsernames([Username.Parse("USR1111111111111111"), Username.Parse("USR2222222222222222")]);

// Act
var resultForUnauthorizedUsername = IdentityDeletionProcessAuditLogEntry.BelongsToUser(unauthorizedUsername).Compile()(auditLogEntry);

// Assert
resultForUnauthorizedUsername.Should().BeFalse();
}
}

0 comments on commit 3fd84b1

Please sign in to comment.