Skip to content

Commit

Permalink
VIH-11041 detect if any participant of endpoint are screened when inv…
Browse files Browse the repository at this point in the history
…iting to a new or existing consultation
  • Loading branch information
Shaed Parkar committed Oct 16, 2024
1 parent ec8904a commit b8437b5
Show file tree
Hide file tree
Showing 23 changed files with 624 additions and 55 deletions.
79 changes: 72 additions & 7 deletions VideoWeb/VideoWeb.Common/Models/Conference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,6 @@ public void AddParticipant(Participant participant)
Participants.Add(participant);
}
}

public void RemoveParticipant(Guid referenceId)
{
Participants.RemoveAll(x => x.RefId == referenceId);
}

public bool IsParticipantInConference(string username)
{
Expand Down Expand Up @@ -164,11 +159,79 @@ private CivilianRoom GetOrCreateCivilianRoom(long roomId)
return room;
}

public CivilianRoom GetRoom(Guid participantId)
/// <summary>
/// Check if any of the participants or endpoints from the given list are screened from each other.
/// If any of the externalReferenceIds for an entity exist in the protectFrom list of another entity, they
/// cannot be in a consultation together.
/// </summary>
/// <param name="participantIds">List of participants to be in a consultation</param>
/// <param name="endpointIds">List of endpoints to be in a consultation</param>
public bool AreEntitiesScreenedFromEachOther(List<Guid> participantIds, List<Guid> endpointIds)
{
var allExternalReferenceIds = Participants.Select(x => x.ExternalReferenceId)
.Union(Endpoints.Select(x => x.ExternalReferenceId)).ToList();

foreach (var participantId in participantIds)
{
var participant = Participants.Find(x => x.Id == participantId);
if (participant == null) continue;
if (participant.ProtectFrom.Exists(allExternalReferenceIds.Contains)) return true;
}

foreach (var endpointId in endpointIds)
{
var endpoint = Endpoints.Find(x => x.Id == endpointId);
if (endpoint == null) continue;
if (endpoint.ProtectFrom.Exists(allExternalReferenceIds.Contains)) return true;
}

return false;
}

/// <summary>
/// Check if the participant is screened from any of the entities in a existing consultation room
/// </summary>
/// <param name="roomLabel">The label of the consultation room</param>
/// <param name="participantId">The id of the participant attempting to join a consultation room</param>
/// <returns>true if the participant is not screened from any of the entities in the room</returns>
public bool CanParticipantJoinConsultationRoom(string roomLabel, Guid participantId)
{
var participant = Participants.Find(x => x.Id == participantId);
if (participant == null)
throw new ArgumentException($"The participant {participantId} does not exist", nameof(participantId));

var ids = GetParticipantsAndEndpointsInRoom(roomLabel);
ids.participantIds.Add(participantId);

return !AreEntitiesScreenedFromEachOther(ids.participantIds, ids.endpointIds);
}

/// <summary>
/// Check if the endpoint is screened from any of the entities in a existing consultation room
/// </summary>
/// <param name="roomLabel">The label of the consultation room</param>
/// <param name="endpointId">The id of the endpoint attempting to join a consultation room</param>
/// <returns>true if the endpoint is not screened from any of the entities in the room</returns>
public bool CanEndpointJoinConsultationRoom(string roomLabel, Guid endpointId)
{
var endpoint = Endpoints.Find(x => x.Id == endpointId);
if (endpoint == null)
throw new ArgumentException($"The endpoint {endpointId} does not exist", nameof(endpointId));

var ids = GetParticipantsAndEndpointsInRoom(roomLabel);
ids.endpointIds.Add(endpointId);

return !AreEntitiesScreenedFromEachOther(ids.participantIds, ids.endpointIds);
}

private (List<Guid> participantIds, List<Guid> endpointIds) GetParticipantsAndEndpointsInRoom(string roomLabel)
{
return CivilianRooms.Find(room => room.Participants.Contains(participantId));
var participants = Participants.Where(x => x.CurrentRoom?.Label == roomLabel).Select(x => x.Id).ToList();
var endpoints = Endpoints.Where(x => x.CurrentRoom?.Label == roomLabel).Select(x => x.Id).ToList();
return (participants, endpoints);
}


public HearingLayout GetRecommendedLayout()
{
var numOfParticipantsIncJudge = Participants.Count + Endpoints.Count;
Expand Down Expand Up @@ -242,5 +305,7 @@ private List<Endpoint> GetNonScreenedEndpoints()

return endpoints;
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ namespace VideoWeb.Contract.Request
{
public class StartPrivateConsultationRequest
{
public Guid[] InviteParticipants { get; set; }
public Guid[] InviteParticipants { get; set; } = [];

public Guid[] InviteEndpoints { get; set; }
public Guid[] InviteEndpoints { get; set; } = [];

public Guid ConferenceId { get; set; }

Expand Down
10 changes: 10 additions & 0 deletions VideoWeb/VideoWeb.Contract/Responses/ParticipantResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,15 @@ public class ParticipantResponse
/// List of participants linked this participant
/// </summary>
public List<LinkedParticipantResponse> LinkedParticipants { get; set; }

/// <summary>
/// A unique identifier for the participant (used by special measures)
/// </summary>
public string ExternalReferenceId { get; set; }

/// <summary>
/// List of external references to protect this participant from
/// </summary>
public List<string> ProtectFrom { get; set; }
}
}
11 changes: 11 additions & 0 deletions VideoWeb/VideoWeb.Contract/Responses/VideoEndpointResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using VideoWeb.Common.Models;

namespace VideoWeb.Contract.Responses;
Expand Down Expand Up @@ -41,4 +42,14 @@ public class VideoEndpointResponse
/// The endpoint's interpreter language, if applicable
/// </summary>
public InterpreterLanguageResponse InterpreterLanguage { get; set; }

/// <summary>
/// A unique identifier for the participant (used by special measures)
/// </summary>
public string ExternalReferenceId { get; set; }

/// <summary>
/// List of external references to protect this participant from
/// </summary>
public List<string> ProtectFrom { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,32 @@ public ConferenceCacheModelBuilder()
.Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Individual)
.With(x => x.Username = Faker.Internet.Email())
.With(x => x.Id = Guid.NewGuid()).Build(),
.With(x => x.Id = Guid.NewGuid())
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Representative)
.With(x => x.Username = Faker.Internet.Email())
.With(x => x.Id = Guid.NewGuid()).Build(),
.With(x => x.Id = Guid.NewGuid())
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Individual)
.With(x => x.Username = Faker.Internet.Email())
.With(x => x.Id = Guid.NewGuid()).Build(),
.With(x => x.Id = Guid.NewGuid())
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Representative)
.With(x => x.Username = Faker.Internet.Email())
.With(x => x.Id = Guid.NewGuid()).Build()
.With(x => x.Id = Guid.NewGuid())
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build()
},
Endpoints = new List<Endpoint>
{
Builder<Endpoint>.CreateNew().With(x => x.Id = Guid.NewGuid()).With(x => x.DisplayName = "EP1")
Builder<Endpoint>.CreateNew()
.With(x => x.Id = Guid.NewGuid())
.With(x => x.DisplayName = "EP1")
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString())
.Build(),
Builder<Endpoint>.CreateNew().With(x => x.Id = Guid.NewGuid()).With(x => x.DisplayName = "EP2")
Builder<Endpoint>.CreateNew()
.With(x => x.Id = Guid.NewGuid())
.With(x => x.DisplayName = "EP2")
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString())
.Build()
},
HearingVenueName = "Hearing Venue Test"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task should_return_accepted()
var request = new AddEndpointConsultationRequest
{
ConferenceId = _testConference.Id,
EndpointId = Guid.NewGuid(),
EndpointId = _testConference.Endpoints[0].Id,
RoomLabel = "RoomLabel"
};

Expand Down Expand Up @@ -81,6 +81,36 @@ public async Task should_return_unauthorised_not_in_conference()
result.Should().BeOfType<UnauthorizedObjectResult>();
}

[Test]
public async Task should_return_forbidden_when_endpoint_is_screened()
{
// arrange
var roomLabel = "RoomLabel1";
var individual = _testConference.Participants.Find(x => x.Role == Role.Individual);
var endpoint = _testConference.Endpoints[0];
individual.ProtectFrom.Add(endpoint.ExternalReferenceId);
var rep = _testConference.Participants.Find(x => x.Role == Role.Representative);
individual.CurrentRoom = new ConsultationRoom {Label = roomLabel};
rep.CurrentRoom = new ConsultationRoom {Label = roomLabel};

SetupControllerWithClaims(rep.Username);
var request = new AddEndpointConsultationRequest
{
ConferenceId = _testConference.Id,
EndpointId = endpoint.Id,
RoomLabel = roomLabel
};

// act
var result = await _sut.AddEndpointToConsultationAsync(request, CancellationToken.None);

// assert
result.Should().BeOfType<ForbidResult>();

_mocker.Mock<IVideoApiClient>().Verify(
x => x.JoinEndpointToConsultationAsync(It.IsAny<EndpointConsultationRequest>(), It.IsAny<CancellationToken>()), Times.Never);
}

[Test]
public async Task should_return_call_JoinEndpointToConsultationAsync_with_correct_params()
{
Expand All @@ -89,7 +119,7 @@ public async Task should_return_call_JoinEndpointToConsultationAsync_with_correc
var request = new AddEndpointConsultationRequest
{
ConferenceId = _testConference.Id,
EndpointId = Guid.NewGuid(),
EndpointId = _testConference.Endpoints[0].Id,
RoomLabel = "RoomLabel"
};

Expand All @@ -115,7 +145,7 @@ public async Task should_return_call_videoapi_response_error_joining()
var request = new AddEndpointConsultationRequest
{
ConferenceId = _testConference.Id,
EndpointId = Guid.NewGuid(),
EndpointId = _testConference.Endpoints[0].Id,
RoomLabel = "RoomLabel"
};
var apiException = new VideoApiException<ProblemDetails>("Bad Request", (int) HttpStatusCode.BadRequest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ public static Conference BuildConferenceForTest()
.With(x => x.Username = "[email protected]")
.Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Individual)
.With(x => x.Id = Guid.NewGuid()).With(x => x.Username = "[email protected]").Build(),
.With(x => x.Id = Guid.NewGuid()).With(x => x.Username = "[email protected]")
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Representative)
.With(x=> x.Username = "[email protected]")
.With(x => x.Id = Guid.NewGuid()).Build(),
.With(x => x.Id = Guid.NewGuid())
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Individual)
.With(x => x.Id = Guid.NewGuid()).Build(),
.With(x => x.Id = Guid.NewGuid())
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Participant>.CreateNew().With(x => x.Role = Role.Representative)
.With(x => x.Id = Guid.NewGuid()).Build(),
.With(x => x.Id = Guid.NewGuid())
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Participant>.CreateNew()
.With(x => x.Role = Role.StaffMember).With(x => x.Id = Guid.NewGuid())
.With(x => x.Username = "[email protected]")
Expand All @@ -39,11 +43,14 @@ public static Conference BuildConferenceForTest()
Endpoints = new List<Endpoint>
{
Builder<Endpoint>.CreateNew().With(x => x.Id = Guid.NewGuid()).With(x => x.DisplayName = "EP1")
.With(x=> x.DefenceAdvocateUsername = "[email protected]").Build(),
.With(x=> x.DefenceAdvocateUsername = "[email protected]")
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Endpoint>.CreateNew().With(x => x.Id = Guid.NewGuid()).With(x => x.DisplayName = "EP2")
.With(x=> x.DefenceAdvocateUsername = "[email protected]").Build(),
.With(x=> x.DefenceAdvocateUsername = "[email protected]")
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
Builder<Endpoint>.CreateNew().With(x => x.Id = Guid.NewGuid()).With(x => x.DisplayName = "EP3")
.With(x=> x.DefenceAdvocateUsername = "[email protected]").Build(),
.With(x=> x.DefenceAdvocateUsername = "[email protected]")
.With(x => x.ExternalReferenceId = Guid.NewGuid().ToString()).Build(),
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -96,11 +97,12 @@ public async Task should_return_accepted_if_user_is_in_consultation()
var cp = new ClaimsPrincipalBuilder().WithRole(AppRoles.RepresentativeRole)
.WithUsername("[email protected]").Build();
_sut = SetupControllerWithClaims(cp);
var participant = _testConference.Participants.First(x => x.Role == Role.Individual);

var request = new InviteToConsultationRequest
{
ConferenceId = _testConference.Id,
ParticipantId = _testConference.Participants[0].Id,
ParticipantId = participant.Id,
RoomLabel = "Room1"
};

Expand All @@ -112,7 +114,42 @@ public async Task should_return_accepted_if_user_is_in_consultation()
_mocker.Mock<IConsultationNotifier>()
.Verify(
x => x.NotifyConsultationRequestAsync(_testConference, "Room1", _testConference.Participants[2].Id,
_testConference.Participants[0].Id), Times.Once);
participant.Id), Times.Once);
}

[Test]
public async Task should_return_forbidden_if_invitee_is_screened_from_existing_participant_in_room()
{
// arrange
var cp = new ClaimsPrincipalBuilder().WithRole(AppRoles.RepresentativeRole)
.WithUsername("[email protected]").Build();
_sut = SetupControllerWithClaims(cp);

var roomLabel = "RoomLabel1";
var individual = _testConference.Participants.Find(x => x.Role == Role.Individual);
var endpoint = _testConference.Endpoints[0];
individual.ProtectFrom.Add(endpoint.ExternalReferenceId);
var rep = _testConference.Participants.Find(x => x.Role == Role.Representative);

rep.CurrentRoom = new ConsultationRoom {Label = roomLabel};
rep.Username = "[email protected]";
endpoint.CurrentRoom = new ConsultationRoom {Label = roomLabel};

var request = new InviteToConsultationRequest
{
ConferenceId = _testConference.Id,
RoomLabel = roomLabel,
ParticipantId = individual.Id
};

// act
var result = await _sut.InviteToConsultationAsync(request, CancellationToken.None);

// assert
result.Should().BeOfType<ForbidResult>();

_mocker.Mock<IConsultationNotifier>().Verify(
x => x.NotifyConsultationRequestAsync(_testConference, roomLabel, Guid.Empty, individual.Id), Times.Never);
}

private ConsultationsController SetupControllerWithClaims(ClaimsPrincipal claimsPrincipal)
Expand Down
Loading

0 comments on commit b8437b5

Please sign in to comment.